TCP State Machine | Networking
Overview
Section titled “Overview”The TCP connection state machine (defined in RFC 793, with updates in RFC 1122) is one of the most Precisely specified protocol behaviors in all of networking. Every TCP endpoint transitions through Defined states as connections are established, used, and torn down. Understanding these states is Essential for troubleshooting connection issues, writing robust network software, and operating High-performance servers.
This document maps every state, every transition, every edge case (simultaneous open, simultaneous Close, half-open connections), and provides practical guidance for diagnosing TCP state problems in Production.
The 11 TCP States
Section titled “The 11 TCP States”TCP defines 11 states. An endpoint is always in exactly one of these states:
| State | Description |
|---|---|
| CLOSED | No connection. Initial state. |
| LISTEN | Waiting for connection requests. |
| SYN_SENT | Sent SYN, waiting for response. |
| SYN_RCVD | Received SYN, sent SYN+ACK, waiting for ACK. |
| ESTABLISHED | Connection open. Data transfer in progress. |
| FIN_WAIT_1 | Initiated close, sent FIN, waiting for ACK or FIN. |
| FIN_WAIT_2 | Received ACK for our FIN, waiting for peer”s FIN. |
| CLOSE_WAIT | Received peer’s FIN, waiting for local close. |
| LAST_ACK | Sent our FIN after peer closed, waiting for ACK. |
| TIME_WAIT | Waited for peer’s ACK after our close. 2*MSL timer. |
| CLOSING | Both sides sent FIN simultaneously, waiting for ACK. |
State Diagram
Section titled “State Diagram”stateDiagram-v2 [*] --> CLOSED CLOSED --> LISTEN : passive_open CLOSED --> SYN_SENT : send_SYN LISTEN --> SYN_RCVD : recv_SYN, send_SYN+ACK SYN_SENT --> ESTABLISHED : recv_SYN+ACK, send_ACK SYN_SENT --> SYN_RCVD : recv_SYN, send_SYN+ACK SYN_RCVD --> ESTABLISHED : recv_ACK LISTEN --> SYN_SENT : send_SYN ESTABLISHED --> FIN_WAIT_1 : close, send_FIN ESTABLISHED --> CLOSE_WAIT : recv_FIN, send_ACK FIN_WAIT_1 --> FIN_WAIT_2 : recv_ACK FIN_WAIT_1 --> CLOSING : recv_FIN, send_ACK FIN_WAIT_1 --> TIME_WAIT : recv_FIN+ACK, send_ACK FIN_WAIT_2 --> TIME_WAIT : recv_FIN, send_ACK CLOSE_WAIT --> LAST_ACK : close, send_FIN LAST_ACK --> CLOSED : recv_ACK CLOSING --> TIME_WAIT : recv_ACK TIME_WAIT --> CLOSED : 2*MSL timeoutNormal Open: The Three-Way Handshake
Section titled “Normal Open: The Three-Way Handshake”The three-way handshake establishes both ends of the connection and synchronizes sequence numbers. Both sides must agree on initial sequence numbers (ISNs) before any data can be exchanged.
State Trace: Client Side
Section titled “State Trace: Client Side”State: CLOSED | |--- SYN (seq=1000) ---------------->| State: SYN_SENT | |<-- SYN+ACK (seq=2000, ack=1001) ---| State: ESTABLISHED (after sending ACK) | |--- ACK (ack=2001) ---------------->| State: ESTABLISHEDState Trace: Server Side
Section titled “State Trace: Server Side”State: LISTEN | |<-- SYN (seq=1000) -----------------| State: SYN_RCVD | |--- SYN+ACK (seq=2000, ack=1001) -->| | |<-- ACK (ack=2001) -----------------| State: ESTABLISHEDWhy Three Messages, Not Two?
Section titled “Why Three Messages, Not Two?”A two-way handshake would be vulnerable to stale duplicate SYNs from old connections. Consider:
- Client sends SYN (seq=1000). Network delay causes it to arrive late.
- Client times out, sends new SYN (seq=3000). Server responds, connection established.
- Data exchanged, connection closed.
- The stale SYN (seq=1000) finally arrives. Server creates a half-open connection.
- Client receives SYN+ACK for a connection it never intended, sends RST.
The three-way handshake prevents this because the client must acknowledge the server’s ISN. The Stale SYN would trigger a SYN+ACK from the server, but the client’s ACK would not match, and the Client would send RST.
ISN Generation
Section titled “ISN Generation”ISNs must be unpredictable to prevent TCP sequence prediction attacks (Blind Spoofing, RFC 6528). Modern implementations use a cryptographically strong PRNG seeded with a combination of:
- Source and destination IP addresses and ports
- A secret key (per-boot random)
- A high-resolution timestamp
Linux uses a 64-bit counter that increments by 1 for each microsecond and by 64,000 for each new Connection, making ISN prediction computationally infeasible.
## View Linux ISN generation parameterssysctl net.ipv4.tcp_timestampssysctl net.ipv4.tcp_syncookiesNormal Close: The Four-Way Teardown
Section titled “Normal Close: The Four-Way Teardown”TCP uses a four-way (full-duplex) close because each direction of the connection must be closed Independently. This is called a “half-close” — one side can stop sending while still receiving.
State Trace: Active Closer (Initiator)
Section titled “State Trace: Active Closer (Initiator)”State: ESTABLISHED | |--- FIN (seq=5000) ---------------->| State: FIN_WAIT_1 | |<-- ACK (ack=5001) -----------------| State: FIN_WAIT_2 | (waiting for peer to close its side) | |<-- FIN (seq=3000) -----------------| State: TIME_WAIT (after sending ACK) | |--- ACK (ack=3001) ---------------->| State: TIME_WAIT (wait 2*MSL)State Trace: Passive Closer
Section titled “State Trace: Passive Closer”State: ESTABLISHED | |<-- FIN (seq=5000) -----------------| State: CLOSE_WAIT | (application can still send data) | |--- ACK (ack=5001) ---------------->| State: CLOSE_WAIT (still, waiting for app to close) | |--- FIN (seq=3000) ---------------->| State: LAST_ACK | |<-- ACK (ack=3001) -----------------| State: CLOSEDFIN_WAIT_2 and Application Hang
Section titled “FIN_WAIT_2 and Application Hang”FIN_WAIT_2 is the state where the active closer has finished sending but is waiting for the peer to Finish. If the peer never sends FIN (application crash, bug, network partition), the endpoint stays In FIN_WAIT_2 indefinitely.
Linux protects against this with tcp_fin_timeout (default 60 seconds). After this timeout, the Kernel forcibly closes the connection and frees the resources.
## View and adjust FIN_WAIT_2 timeoutsysctl net.ipv4.tcp_fin_timeout
# Count connections in FIN_WAIT_2 statess -tan state fin-wait-2 | wc -lIntuition
Section titled “Intuition”The TCP state machine is the lifecycle of a TCP connection. Think of it as a phone call: you dial (SYN), the other person answers (SYN-ACK), you confirm (ACK), you talk (ESTABLISHED), one party hangs up (FIN), the other acknowledges (ACK), and both sides close (TIME-WAIT). Each state represents where the connection is in this process. Understanding the state machine is essential for debugging connection issues: a connection stuck in SYN-SENT means the server is unreachable, while TIME-WAIT is normal cleanup.