A socket is the programming interface to the network — the endpoint your code reads from and writes to. Learn the BSD socket API that every language wraps: create, bind, listen, accept, connect.
A socket is a file-like handle representing one endpoint of a network connection — you read and write bytes to it like a file, and the OS handles moving them over the network. The Berkeley (BSD) socket API, created in the 1980s, defines the standard calls (socket, bind, listen, accept, connect, send, recv), and virtually every language’s networking is a thin wrapper over it. Learn it once and it transfers everywhere.
A SOCKET = a file-like handle (a "descriptor") for a network endpoint.
You read()/write() bytes to it; the OS moves them over the network.
The BSD socket API (the universal standard, wrapped by every language):
socket() create a socket (pick TCP or UDP, IPv4 or IPv6)
bind() attach it to a local (ip, port) [server]
listen() mark it ready to accept connections (TCP) [server]
accept() take the next incoming connection -> a NEW socket [server]
connect() initiate a connection to a server [client]
send()/recv() TCP: stream I/O
sendto()/recvfrom() UDP: per-datagram I/O
close() release the socket
Python/Go/Java/C# all wrap these. Learn the flow once; it's everywhere.A TCP client is short: create a socket, connect to the server’s address and port, then send and receive bytes. Because TCP is a stream, recv gives you whatever bytes have arrived — possibly a partial message or several messages stuck together — so real clients loop and frame messages. This connect-send-recv shape is the template for talking to any TCP server.
import socket
# create a TCP (SOCK_STREAM) IPv4 socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("127.0.0.1", 7777)) # connect to server ip:port
s.sendall(b"hello
") # send bytes (sendall handles partial sends)
data = s.recv(1024) # receive up to 1024 bytes (may be partial!)
print(data.decode())
s.close()
# TCP is a STREAM: recv() returns whatever arrived — could be half a message or
# two messages joined. Real clients LOOP + FRAME messages (next lesson).A server binds to a port, listens, and then accepts connections in a loop — each accept returns a fresh socket dedicated to one client. This basic single-threaded server handles one client at a time; serving many simultaneously requires concurrency (threads or an event loop), which the Server Concurrency course covers. This is the skeleton every network server grows from.
import socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # reuse the port on restart
server.bind(("0.0.0.0", 7777)) # listen on all interfaces, port 7777
server.listen() # ready for connections
while True:
conn, addr = server.accept() # blocks until a client connects
print("client:", addr)
data = conn.recv(1024)
conn.sendall(b"echo: " + data) # respond
conn.close()
# this serves ONE client at a time. Many clients at once -> concurrency
# (threads / an event loop) — see the Server Concurrency course.Under the friendly wrappers, the raw C API is what everything compiles down to (and what a C++/Windows game server may use directly, via Winsock on Windows). It is more verbose — you manage address structs and check return codes — but the sequence is identical to the Python version. Recognizing the C calls helps when reading engine networking code or system documentation.
// the raw BSD sockets flow in C (Python/Go/etc. wrap exactly this):
int fd = socket(AF_INET, SOCK_STREAM, 0); // create
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_port = htons(7777); // htons = host->network byte order
addr.sin_addr.s_addr = INADDR_ANY;
bind(fd, (struct sockaddr*)&addr, sizeof(addr));
listen(fd, 128); // backlog of 128 pending conns
int client = accept(fd, NULL, NULL); // next connection -> new fd
char buf[1024];
recv(client, buf, sizeof(buf), 0);
send(client, "echo", 4, 0);
close(client);
// same calls, more ceremony. On Windows it's "Winsock" (WSAStartup + these).