Topic Overview
Three-Way Handshake (TCP)
Learn how TCP establishes connections using the three-way handshake (SYN, SYN-ACK, ACK).
The three-way handshake is the process TCP uses to establish a reliable connection between a client and server before data transmission begins.
The Process
Step 1: SYN (Synchronize)
Client → Server
- Client sends SYN packet with initial sequence number (ISN)
- Client enters SYN_SENT state
Step 2: SYN-ACK (Synchronize-Acknowledge)
Server → Client
- Server receives SYN, sends SYN-ACK
- Server sends its own ISN and acknowledges client's ISN + 1
- Server enters SYN_RECEIVED state
Step 3: ACK (Acknowledge)
Client → Server
- Client receives SYN-ACK, sends ACK
- Client acknowledges server's ISN + 1
- Both enter ESTABLISHED state
- Connection is now ready for data transfer
Sequence Diagram
Client Server
| |
|-------- SYN (seq=x) ---->|
| | (SYN_RECEIVED)
|<-- SYN-ACK (seq=y, |
| ack=x+1) -----------|
| (SYN_SENT) |
|-------- ACK (ack=y+1) -->|
| (ESTABLISHED) | (ESTABLISHED)
| |
|<===== Data Transfer ===>|
Examples
TCP Connection in Code
// Client side
const socket = new Socket();
socket.connect(80, 'example.com', () => {
// Three-way handshake completed
console.log('Connected!');
socket.write('GET / HTTP/1.1\r\n\r\n');
});
Wireshark Capture
1. Client → Server: SYN, Seq=1000
2. Server → Client: SYN-ACK, Seq=5000, Ack=1001
3. Client → Server: ACK, Ack=5001
Common Pitfalls
- SYN flood attacks: Too many SYN packets without completing handshake. Fix: SYN cookies, rate limiting
- Not handling timeouts: Client waits forever if server doesn't respond. Fix: Set connection timeout
- Sequence number confusion: Not understanding ISN selection. Fix: Random ISN for security
- State machine errors: Not tracking connection state correctly. Fix: Proper state management
Interview Questions
Beginner
Q: What is the TCP three-way handshake and why is it needed?
A: The three-way handshake is the process TCP uses to establish a connection:
- Client sends SYN: "I want to connect, my sequence number is X"
- Server sends SYN-ACK: "I acknowledge your SYN (X+1), my sequence number is Y"
- Client sends ACK: "I acknowledge your SYN (Y+1)"
Why needed:
- Synchronize sequence numbers: Both sides agree on starting sequence numbers
- Establish connection: Both sides confirm they're ready to communicate
- Reliability: Ensures both sides can send and receive data
Intermediate
Q: What happens if the ACK packet in the three-way handshake is lost?
A:
Scenario: Client sends ACK, but it's lost in transit.
What happens:
- Server doesn't receive ACK, remains in SYN_RECEIVED state
- Server will retransmit SYN-ACK (with exponential backoff)
- Client receives duplicate SYN-ACK, sends another ACK
- Connection eventually establishes
Timeout handling:
- Server has timeout (typically 30-120 seconds)
- If no ACK received, server closes connection
- Client can retry connection
Key point: TCP is reliable - lost packets are retransmitted until acknowledged.
Senior
Q: Design a high-performance TCP server that handles millions of connections. How do you optimize the three-way handshake? Handle SYN flood attacks?
A:
Optimizations:
class HighPerformanceTCPServer {
// 1. SYN Cookies (prevent SYN flood)
async handleSYN(synPacket: SYN): Promise<void> {
// Instead of storing connection state, use SYN cookie
const cookie = this.generateSYNCookie(synPacket);
// Send SYN-ACK with cookie (no state stored)
await this.sendSYNACK(synPacket, cookie);
}
generateSYNCookie(syn: SYN): number {
// Cryptographic hash of client info + secret
return hash(syn.sourceIP + syn.sourcePort + this.secret);
}
// 2. Connection pooling
private connectionPool: Map<string, Connection[]> = new Map();
async getConnection(clientId: string): Promise<Connection> {
const pool = this.connectionPool.get(clientId);
if (pool && pool.length > 0) {
return pool.pop()!; // Reuse connection
}
return await this.createNewConnection();
}
// 3. Fast open (TFO) - skip handshake for repeat clients
async handleWithTFO(packet: Packet): Promise<void> {
if (this.hasValidTFO(packet)) {
// Skip handshake, start sending data immediately
return this.processData(packet);
}
// Fall back to normal handshake
return this.normalHandshake(packet);
}
// 4. Load balancing across workers
async distributeConnection(syn: SYN): Promise<void> {
const workerId = this.selectWorker(syn.sourceIP);
await this.workers[workerId].handleSYN(syn);
}
// 5. Rate limiting
private synRateLimiter: RateLimiter = new RateLimiter();
async handleSYNWithRateLimit(syn: SYN): Promise<void> {
if (!this.synRateLimiter.allow(syn.sourceIP)) {
// Drop SYN, prevent flood
return;
}
await this.handleSYN(syn);
}
}
SYN Flood Protection:
- SYN Cookies: Don't store state until ACK received
- Rate limiting: Limit SYNs per IP
- Connection limits: Max connections per IP
- Firewall rules: Block suspicious IPs
- DDoS protection: Use CDN/proxy for filtering
Performance:
- Epoll/kqueue: Efficient I/O multiplexing
- Zero-copy: Minimize data copying
- Connection reuse: Keep-alive, connection pooling
- Worker processes: Distribute load across CPUs
Key Takeaways
- Three-way handshake establishes TCP connection: SYN → SYN-ACK → ACK
- Synchronizes sequence numbers: Both sides agree on starting sequence numbers
- Reliable: Lost packets are retransmitted until acknowledged
- State tracking: Both sides track connection state (SYN_SENT, SYN_RECEIVED, ESTABLISHED)
- Security: Random ISN prevents sequence number prediction attacks
- Optimization: SYN cookies, TCP Fast Open, connection pooling for performance
- SYN flood protection: Rate limiting, SYN cookies, connection limits