Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Socket Programming  ›  Lesson

Socket Descriptors — Handles and Blocking

Descriptor 12 minA program holds a socket only as a small integer descriptor or handle that the kernel maps to the real socket, and non-blocking calls let one tick loop read every waiting message without ever stalling.

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 recvfrom waits 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.

One tick of the match serverStep 1 of 4
  1. Drain

    A non-blocking recvfrom runs 14 times and collects 14 datagrams. The 15th call reports "would block" immediately.

Drills

Answer fast — trust the rule.

quick drill

A freshly started Linux program opens its first socket. Which descriptor number will it most likely get?

quick drill

The tick loop uses a blocking recvfrom, and no player sends anything for 2 seconds. What happens?

quick drill

What does a non-blocking recvfrom do when no datagram is waiting?

quick drill

After six hours of running, the lobby starts failing with "Too many open files". What is the likely cause?

quick drill

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.

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainNon-blocking reads and closed sockets

Explain why the Manja Arena match server's tick loop uses non-blocking sockets, and why the lobby must close every socket when a player leaves.