The TCP 3-Way Handshake Explained
Before TCP can transfer data, both endpoints must agree on initial sequence numbers and confirm they can both send AND receive. This negotiation is the famous 3-way handshake. It happens billions of times per second across the internet.
The three packets
Client Server
| |
| SYN seq=x |
|─────────────────────────────────────→|
| |
| SYN-ACK seq=y, ack=x+1 |
|←─────────────────────────────────────|
| |
| ACK seq=x+1, ack=y+1 |
|─────────────────────────────────────→|
| |
| [connection established] |
| [data can now flow] |
Step by step
1. Client → Server: SYN
“I want to start a connection. My initial sequence number is X.”
The SYN flag is set. The Sequence Number field has a random value (X).
2. Server → Client: SYN-ACK
“OK. I acknowledge your SYN (ACK = X+1, meaning ‘I’m expecting X+1 next’). My initial sequence number is Y.”
Both SYN and ACK flags are set. Sequence Number = Y, Acknowledgment Number = X+1.
3. Client → Server: ACK
“OK. I acknowledge your SYN (ACK = Y+1).”
ACK flag set. Sequence Number = X+1, Acknowledgment Number = Y+1.
Why three packets
You need three because each side has to:
- Send its own initial sequence number
- Confirm receipt of the other side’s sequence number
That’s 4 logical operations, but the server’s “send my SYN” and “ack your SYN” are combined into one packet (the SYN-ACK), giving us 3 packets total.
The latency cost: 1 RTT
Notice that no application data has been sent yet — and we’ve already used 1.5 round trips (the client sent the third ACK, then can immediately send data, so effectively 1 RTT before the first data byte).
If you’re connecting to a server 200 ms away, you’ve already waited 200 ms before HTTP can even start.
Why TLS makes it worse
HTTPS adds another 1-2 RTTs for the TLS handshake on top of TCP. So a fresh HTTPS connection costs:
TCP handshake: 1 RTT
TLS 1.2 handshake: 2 RTTs
First HTTP request: ½ RTT
Total before first byte: ~3.5 RTTs
At 100 ms RTT, that's 350 ms before any content arrives.
At 300 ms RTT (mobile in remote area), 1+ second.
TLS 1.3 cuts this to 1 RTT. QUIC (HTTP/3) gets it down to 0 RTT for repeat connections by combining handshakes.
Connection teardown: 4 packets
Closing a connection takes a 4-way handshake (FIN, ACK, FIN, ACK):
Client Server
| FIN → |
| ← ACK |
| ← FIN |
| ACK → |
| [connection closed] |
SYN flood attack
Because servers must allocate state when they receive a SYN (to track the half-open connection), an attacker can flood SYNs without responding. Server resources fill up. Defense: SYN cookies — the server doesn’t allocate state until it receives the final ACK.
# Enable SYN cookies (already on by default in modern Linux)
sysctl net.ipv4.tcp_syncookies # should be 1
Watch a handshake live
# In one terminal, listen on port 8000
nc -l 8000
# In another, capture the handshake
sudo tcpdump -i lo -nn 'tcp port 8000' -c 6
# In a third, connect
nc localhost 8000
# tcpdump shows: SYN, SYN-ACK, ACK, then your data
What to learn next
Ports and sockets — the addressing system within an IP that tells the kernel which application gets which packet. Up next.