TCP State Machine

TCP connections move through a defined set of states. Understanding the state machine explains two common server problems: TIME_WAIT and the accept queue.

Key states

StateWhen
LISTENServer called listen(), waiting for SYN
SYN_RCVDSYN received, SYN-ACK sent (on the SYN queue)
ESTABLISHEDThree-way handshake complete
FIN_WAIT_1/2Active closer sent FIN
TIME_WAITActive closer waiting to ensure ACK delivered (~2 MSL = ~60s)
CLOSE_WAITPassive closer received FIN, app hasn’t called close()

TIME_WAIT and SO_REUSEADDR

When a server restarts after a crash, its port is in TIME_WAIT for ~60 seconds. Without SO_REUSEADDR, bind() returns EADDRINUSE. Always set SO_REUSEADDR before bind() — this is unconditional practice.

int yes = 1;
setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
bind(server, ...);

Accept queue model

The kernel maintains two queues:

  1. SYN queue — connections mid-handshake (SYN_RCVD)
  2. Accept queue — fully established connections waiting for accept()

The backlog argument to listen() sizes the accept queue. When it fills, new SYNs are silently dropped (Linux) or SYN-ACKed but with backpressure.

In the curriculum

Covered in m08-sockets-networking and the PDF ch09 Going Deeper section (accept queue model, SYN queue vs accept queue, backlog, thundering herd).

See also

sockets, select-poll-epoll