Socket Descriptors — Handles and Blocking
In the last lesson, socket() returned fd. That small number is all your program ever holds of a socket; the real socket, with its buffers, state and queues, lives inside the operating system's kernel. How you treat that number decides whether the match server ticks smoothly or freezes.
The rule
A socket is just a descriptor; never let a call on it stall the tick.
On Unix-like systems a socket is a file descriptor: a small integer that indexes a table the kernel keeps for each process. Files, pipes and sockets all share that table. Descriptors 0, 1 and 2 are already taken by standard input, output and error, and the kernel hands out the lowest free number, so the first socket a program opens is usually 3. Windows uses an opaque SOCKET handle instead, but the idea is the same.
Three facts follow.
- They are limited. A process may hold only so many at once; on many Linux systems the default limit is 1,024. A lobby with 20,000 TCP players needs that limit raised.
- They leak. Forget to close a socket and its descriptor stays taken. Leak one per disconnect, and hours later the lobby fails with "Too many open files".
- Calls on them block by default. A plain
recvfromwaits until a datagram arrives. In a tick loop that is fatal: if the arena goes quiet for two seconds, the server stops ticking for two seconds.
The fix for the last one is non-blocking mode. A non-blocking read returns at once with an error that means "nothing to read right now" (EAGAIN or EWOULDBLOCK in C, BlockingIOError in Python), and the loop carries on. Here is the drain_inbox that the tick loop in the first section called:
sock.setblocking(False)
def drain_inbox(sock):
received = []
while True:
try:
data, addr = sock.recvfrom(1500)
except BlockingIOError: # nothing left for this tick
return received
received.append((addr, data))Servers that watch thousands of sockets can also ask the kernel which descriptors are ready, using calls such as select, poll, epoll or kqueue. Later parts of the roadmap build on those.
The shortcut
In a tick loop, drain, don't wait: read until "would block", then get on with the tick.
Drain
A non-blocking
recvfromruns 14 times and collects 14 datagrams. The 15th call reports "would block" immediately.
Drills
Answer fast — trust the rule.
A freshly started Linux program opens its first socket. Which descriptor number will it most likely get?
The tick loop uses a blocking recvfrom, and no player sends anything for 2 seconds. What happens?
What does a non-blocking recvfrom do when no datagram is waiting?
After six hours of running, the lobby starts failing with "Too many open files". What is the likely cause?
What does a Windows program hold in place of an integer file descriptor?
Remember
- A socket is just a descriptor; never let a call on it stall the tick.
- In a tick loop, drain until "would block", then get on with the tick.
- Trap: leaking descriptors, or blocking reads inside the game loop.
