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:

  1. Client sends SYN: "I want to connect, my sequence number is X"
  2. Server sends SYN-ACK: "I acknowledge your SYN (X+1), my sequence number is Y"
  3. 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:

  1. SYN Cookies: Don't store state until ACK received
  2. Rate limiting: Limit SYNs per IP
  3. Connection limits: Max connections per IP
  4. Firewall rules: Block suspicious IPs
  5. 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

About the author

InterviewCrafted helps you master system design with patience. We believe in curiosity-led engineering, reflective writing, and designing systems that make future changes feel calm.