← All lab notes

Understanding the TCP Three-Way Handshake

Every TCP connection begins with a three-way handshake. The client and server synchronize sequence numbers before any application data flows.

The handshake as a sequence

100%
ServerClientServerClientLISTEN → SYN-RECEIVEDSYN-SENT → ESTABLISHEDConnection establishedSYN (seq=x)1SYN+ACK (seq=y, ack=x+1)2ACK (ack=y+1)3
Diagram source
sequenceDiagram
    autonumber
    participant C as Client
    participant S as Server
    C->>S: SYN (seq=x)
    Note right of S: LISTEN → SYN-RECEIVED
    S-->>C: SYN+ACK (seq=y, ack=x+1)
    Note left of C: SYN-SENT → ESTABLISHED
    C->>S: ACK (ack=y+1)
    Note over C,S: Connection established

Connection state machine

100%

active open / send SYN

passive open

recv SYN+ACK / send ACK

recv SYN / send SYN+ACK

recv ACK

close / send FIN

recv ACK

recv FIN / send ACK

timeout (2*MSL)

CLOSED

SYN_SENT

LISTEN

ESTABLISHED

SYN_RCVD

FIN_WAIT_1

FIN_WAIT_2

TIME_WAIT

Diagram source
stateDiagram-v2
    [*] --> CLOSED
    CLOSED --> SYN_SENT: active open / send SYN
    CLOSED --> LISTEN: passive open
    SYN_SENT --> ESTABLISHED: recv SYN+ACK / send ACK
    LISTEN --> SYN_RCVD: recv SYN / send SYN+ACK
    SYN_RCVD --> ESTABLISHED: recv ACK
    ESTABLISHED --> FIN_WAIT_1: close / send FIN
    FIN_WAIT_1 --> FIN_WAIT_2: recv ACK
    FIN_WAIT_2 --> TIME_WAIT: recv FIN / send ACK
    TIME_WAIT --> [*]: timeout (2*MSL)

Why three segments and not two?

A two-way handshake cannot distinguish an old duplicate SYN from a new one. The final ACK lets the server reject stale connection attempts.

100%

LISTEN

SYN_RCVD

Old duplicate SYN

Server state?

Would wrongly accept

in 2-way handshake

RST: duplicate detected

3-way handshake saves us

Diagram source
flowchart LR
    A[Old duplicate SYN] --> B{Server state?}
    B -- LISTEN --> C[Would wrongly accept\nin 2-way handshake]
    B -- SYN_RCVD --> D[RST: duplicate detected\n3-way handshake saves us]

Handshake timing

100%
000500000500000500000500000Send SYN Listen Wait SYN+ACK Reply SYN+ACK Send ACK Established ClientServerConnection establishment timeline
Diagram source
gantt
    title Connection establishment timeline
    dateFormat X
    axisFormat %L
    section Client
    Send SYN           :active, c1, 0, 4
    Wait SYN+ACK       :c2, 4, 4
    Send ACK           :done, c3, 8, 4
    section Server
    Listen             :s1, 0, 4
    Reply SYN+ACK      :active, s2, 4, 4
    Established        :done, s3, 8, 4

Packet capture snippet

The same exchange on the wire, captured with tcpdump:

bash
$ sudo tcpdump -i eth0 'tcp[tcpflags] & (tcp-syn|tcp-fin) != 0'
IP 10.0.0.5.51734 > 93.184.216.34.443: Flags [S], seq 2834219942
IP 93.184.216.34.443 > 10.0.0.5.51734: Flags [S.], seq 1113625201, ack 2834219943
IP 10.0.0.5.51734 > 93.184.216.34.443: Flags [.], ack 1113625202

Key insight: the handshake is not “three messages for politeness” — it is a minimal agreement protocol that survives duplicated, delayed, and reordered packets on an unreliable network.