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
| State | When |
|---|---|
LISTEN | Server called listen(), waiting for SYN |
SYN_RCVD | SYN received, SYN-ACK sent (on the SYN queue) |
ESTABLISHED | Three-way handshake complete |
FIN_WAIT_1/2 | Active closer sent FIN |
TIME_WAIT | Active closer waiting to ensure ACK delivered (~2 MSL = ~60s) |
CLOSE_WAIT | Passive 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:
- SYN queue — connections mid-handshake (
SYN_RCVD) - 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).