Why a handshake?
TCP (Transmission Control Protocol) promises reliable, ordered delivery over an unreliable network. To do that, both sides number every byte they send with sequence numbers, and acknowledge what they receive with ACK numbers.
Before sending data, the two computers must agree on their starting sequence numbers. That agreement is the 3-way handshake.
The three steps
Client Server (LISTEN)
| ---- SYN, seq = x -----------------> | client: SYN-SENT
| <--- SYN-ACK, seq = y, ack = x + 1 -- | server: SYN-RECEIVED
| ---- ACK, seq = x + 1, ack = y + 1 --> | both: ESTABLISHED
- SYN — “I want to connect; my first sequence number is x.”
- SYN-ACK — “OK. I got x (I expect x + 1 next), and my first number is y.”
- ACK — “Got y. Let’s go.”
Starting numbers are random, which protects against confusing old, delayed packets with a new connection (and against some attacks).
Why not two steps?
With only SYN and SYN-ACK, the server would never know whether its own starting number y reached the client. Each side needs to send its number and see it acknowledged — that takes three messages.
Data transfer
Every data segment carries a sequence number; the receiver replies with an ACK = next byte expected. If the client sends 100 bytes starting at 101, the server answers ack = 201. Missing ACKs trigger retransmission. To avoid waiting for every single ACK, TCP keeps several segments in flight using a sliding window.
Closing: the 4-way handshake
Each direction is closed separately:
- Client → FIN (I’m done sending) → FIN-WAIT-1
- Server → ACK → server CLOSE-WAIT, client FIN-WAIT-2
- Server → FIN (I’m done too) → LAST-ACK
- Client → ACK → client waits in TIME-WAIT (to catch a lost final ACK), then both CLOSED
Key TCP states
| State | Meaning |
|---|---|
| LISTEN | Server waiting for connections |
| SYN-SENT / SYN-RECEIVED | Handshake in progress |
| ESTABLISHED | Connection open — data can flow |
| FIN-WAIT, CLOSE-WAIT, LAST-ACK | Closing |
| TIME-WAIT | Waiting a short while after closing |
See it on your own computer
# Linux / macOS: list TCP connections and their states
netstat -an | grep -E "LISTEN|ESTABLISHED"
A tiny TCP client in Python — the handshake happens inside connect():
import socket
s = socket.create_connection(("example.com", 80)) # SYN, SYN-ACK, ACK happen here
s.sendall(b"GET / HTTP/1.0\r\nHost: example.com\r\n\r\n")
print(s.recv(200))
s.close() # FIN / ACK exchange
TCP vs UDP
| TCP | UDP | |
|---|---|---|
| Connection setup | 3-way handshake | None |
| Reliability & order | ✅ | ❌ |
| Speed / overhead | Higher overhead | Very light |
| Used for | Web, email, file transfer | Video calls, gaming, DNS |
Common mistakes
- Forgetting the +1 in ACK numbers (a SYN or FIN counts as one sequence number).
- Mixing up which side sends the SYN-ACK (always the side that received the SYN).
- Thinking closing also takes three messages — it usually takes four.
Complexity at a glance
| Case / operation | Time | Why |
|---|---|---|
| Messages to open a connection | 3 | SYN, SYN-ACK, ACK. |
| Messages to close | 4 | FIN, ACK, FIN, ACK. |
| Delay before data can flow | 1 round-trip time |
Quick check
Test yourself — pick an answer to see if you got it.
1. What is the correct order of the TCP handshake?
The client sends SYN, the server answers SYN-ACK, the client confirms with ACK.
2. The client's SYN has seq = 100. What ack number does the server's SYN-ACK carry?
The ACK number is the next sequence number expected — 100 + 1.
3. Why does TCP need a handshake but UDP does not?
UDP just sends datagrams with no setup, no ordering and no retransmission.
4. Which state does a server wait in before any client connects?
A server socket listens on a port, waiting for SYN segments.