TCP echo: The three syscalls
Every TCP server in every language is the same three syscalls. socket() to create the file descriptor. bind() + listen() to claim the port. accept() to wait for a client. Once you can echo, you can do everything.
import socket
HOST = "127.0.0.1"
PORT = 6380
def handle_client(client, addr):
with client:
while True:
data = client.recv(4096)
if not data:
break
client.sendall(data)
def main():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind((HOST, PORT))
server.listen(1)
while True:
client, addr = server.accept()
handle_client(client, addr)The whole server. SOCK_STREAM is the constant that says TCP. SO_REUSEADDR makes restart-edit-restart loops painless.
Three concepts you have to internalise. recv(4096) blocks until SOMETHING arrives, possibly less than 4096 bytes. sendall() loops internally until every byte is written. The empty-bytes return from recv means the client closed the connection cleanly.
handle_client runs in the main thread. While it is reading or echoing for one client, the next accept() never runs. The kernel queues the next connection (backlog of 1 in listen(1)) but anything past that gets refused. We replace this with the event loop at step 9.
make 01-tcp-echo
# In another terminal:
nc localhost 6380
> hello
< helloRun the echo server, then talk to it from netcat.
Quiz: Quiz
Loading practice…
AI prompt: Try it: write a Python client
Loading practice…