Skip to content

Latest commit

 

History

History
43 lines (24 loc) · 2.32 KB

File metadata and controls

43 lines (24 loc) · 2.32 KB

01 Guide: How a TCP server actually works

The three syscalls of every server

Every TCP server in every language is the same three syscalls:

  1. socket() — ask the kernel for a file descriptor that speaks TCP
  2. bind() + listen() — claim a port and tell the kernel you're ready
  3. accept() — block until someone connects, return a new socket for that conversation

socket.SOCK_STREAM is the constant that says "TCP" (as opposed to SOCK_DGRAM for UDP). AF_INET is "IPv4". You'd swap to AF_INET6 for IPv6, but the rest is identical.

SO_REUSEADDR

When you stop a server, the kernel keeps the port reserved for a minute or two in case the network has packets in flight (TIME_WAIT). If you immediately restart, you get Address already in use. Setting SO_REUSEADDR to 1 tells the kernel "I know what I'm doing, give me the port back now."

In production you usually want this on. In development it makes restart-edit-restart loops painless.

Why the server can only handle one client

handle_client runs inside the main thread. While it's reading or echoing for one client, the next accept() call in main() never runs. The kernel queues the next connection in the backlog (size 1 here, from listen(1)), but the next-next connection gets refused.

The fix in real Redis is the event loop, which we add in step 9. Until then we keep the simplicity.

recv and sendall

recv(4096) blocks until at least one byte arrives, then returns up to 4096 bytes. It might return fewer. It might return zero, which means the client closed the connection cleanly (EOF).

sendall(data) blocks until every byte is written. Without it, send() might write only some of the bytes if the OS buffer fills up, and you have to loop yourself.

What "blocking" really means

These calls don't busy-wait. The thread is suspended by the kernel and woken up when data arrives. Single-client blocking is cheap on CPU. It's just wasteful of parallelism, because the same thread can't service two clients at once.

Mental model going forward

Picture this code as a tube. Bytes go in one end, the same bytes come out the other. The next steps:

  • Step 2 reads structured Redis commands out of the byte stream
  • Step 3 stores the data in a dict and responds
  • Step 9 stops blocking so many tubes can run in parallel

Everything else is refinement.