TCP Handshake

The three-way handshake that establishes a reliable, ordered byte stream.

At a Glance

Sequence Diagram

Client picks ISN x; server picks ISN y. Each side ACKs the other's sequence + 1 (SYN consumes one sequence number).

sequenceDiagram participant C as Client participant S as Server Note over C: CLOSED Note over S: LISTEN C->>S: SYN (seq=x) Note over C: SYN-SENT Note over S: SYN-RECEIVED S->>C: SYN-ACK (seq=y, ack=x+1) Note over C: ESTABLISHED C->>S: ACK (seq=x+1, ack=y+1) Note over S: ESTABLISHED Note over C,S: data flows

Connection Identity: the 4-tuple

A TCP connection isn't named by a single "handle" — it's identified by the four values in the IP and TCP headers that distinguish it from every other connection on the host.

The 4-tuple is:

( source IP, source port, destination IP, destination port )

The kernel's TCP table is keyed on exactly this quadruple. Implications that flow from it:

Segment Flags

Six bits in the TCP header that shape how a segment is interpreted.

FlagMeaningWhen set
SYNSynchronize sequence numbersOnly on the first segment from each side during handshake.
ACKAcknowledgment field is validOn every segment after the initial SYN.
FINNo more data from senderGraceful half-close; peer may still send.
RSTAbort — tear down immediatelyOn unexpected segment, closed port, or explicit reset.
PSHDeliver buffered data to app nowSender hints the receiver not to wait for more.
URGUrgent pointer is validRarely used in practice; many stacks ignore it.

State Transitions

The handshake is the first half of the TCP state machine. Each row is one transition.

SideFromToTrigger
ServerCLOSEDLISTENPassive open — listen() called.
ClientCLOSEDSYN-SENTActive open — connect() sends SYN.
ServerLISTENSYN-RECEIVEDReceived SYN; replied with SYN-ACK.
ClientSYN-SENTESTABLISHEDReceived SYN-ACK; sent final ACK.
ServerSYN-RECEIVEDESTABLISHEDReceived client's ACK.

What's Negotiated

TCP options on the SYN and SYN-ACK. Each side offers; both effectively take the minimum or the intersection.

OptionTypical valuePurpose
Initial sequence number (ISN)Random 32-bitRandomized per RFC 6528 to prevent spoofing and confusion with prior connection instances.
Maximum Segment Size (MSS)1460 bytes (Ethernet)Largest TCP payload each side will accept. Usually MTU - 40 (IPv4+TCP headers).
Window Scale7–14 (shift count)Left-shifts the 16-bit window field so receive windows >64KB are possible. Required for high-BDP links.
SACK PermittedOn by defaultEnables Selective ACKs so the receiver can acknowledge non-contiguous ranges, avoiding full-window retransmits.
TimestampsOn by defaultBetter RTT estimates (smoothed RTT for retransmit timer) and PAWS: protect against wrapped sequence numbers on fast links.

Connection Termination

Each direction of the stream closes independently — hence four segments (or three if the FIN-ACK is combined).

sequenceDiagram participant C as Client participant S as Server Note over C,S: ESTABLISHED C->>S: FIN Note over C: FIN-WAIT-1 Note over S: CLOSE-WAIT S->>C: ACK Note over C: FIN-WAIT-2 Note over S: (server app calls close) S->>C: FIN Note over S: LAST-ACK C->>S: ACK Note over S: CLOSED Note over C: TIME-WAIT (2 × MSL) Note over C: CLOSED

Why TIME-WAIT exists

The initiator of the close sits in TIME-WAIT for 2×MSL (Maximum Segment Lifetime, typically 60–120s). Two reasons:

Consequence: a server that initiates many closes accumulates TIME-WAIT sockets. Production tuning usually involves SO_REUSEADDR, tcp_tw_reuse, or making the client close first.

Edge Cases

Simultaneous open

Both sides connect() to each other at the same time. Each receives a SYN in state SYN-SENT; both transition through SYN-RECEIVED and eventually ESTABLISHED. Rare in practice but the state machine handles it.

Half-open connections

One side crashed or had its route torn down; the other still thinks the connection is ESTABLISHED. Detected only when the live side tries to send — peer responds with RST (if it was rebooted) or nothing (if it vanished, needing a keepalive to detect).

SYN flood / SYN cookies

An attacker sends many SYNs with spoofed source addresses; server allocates state for each half-open connection and eventually its SYN queue fills, denying service. Mitigation: SYN cookies — encode connection state into the server's ISN via an HMAC, so no server-side memory is allocated until the client completes the handshake with a matching ACK.

TCP Fast Open (TFO)

Skip the handshake cost for repeat peers. First connection: server mints a TFO cookie during the handshake. Subsequent connections: client sends SYN + cookie + data in one segment; server accepts the data before the 3WHS completes. Saves 1 RTT, but only useful if the request-response is idempotent (replay risk if the SYN+data is retransmitted).

References