Every time you open a browser tab, your OS silently performs a three-way handshake before a single byte of HTTP data is sent. I’ve read about SYN, SYN-ACK, ACK dozens of times — but it didn’t click until I watched it happen in a packet capture.

The Problem TCP Solves

UDP is simple: you send a packet, you hope it arrives. TCP is a contract. Before any data flows, both sides agree they’re ready to talk, synchronize their sequence numbers, and establish state on both ends.

The handshake does three things simultaneously:

  • Proves both sides can send and receive
  • Exchanges initial sequence numbers (ISNs)
  • Allocates socket buffers on both ends

Step by Step

SYN — The client picks a random ISN (let’s call it x) and sends a segment with the SYN flag set and seq=x. No data yet.

SYN-ACK — The server receives it, picks its own ISN y, and responds with both SYN and ACK flags set: seq=y, ack=x+1. The ack=x+1 means “I got your byte x, send me x+1 next.”

ACK — The client acknowledges the server’s ISN: seq=x+1, ack=y+1. Connection is now established.

/* Watching it happen with a raw socket */
struct tcphdr *tcp = (struct tcphdr *)(packet + ip_header_len);

if (tcp->syn && !tcp->ack)
    printf("SYN     seq=%u\n", ntohl(tcp->seq));
else if (tcp->syn && tcp->ack)
    printf("SYN-ACK seq=%u ack=%u\n", ntohl(tcp->seq), ntohl(tcp->ack_seq));
else if (tcp->ack && !tcp->syn)
    printf("ACK     seq=%u ack=%u\n", ntohl(tcp->seq), ntohl(tcp->ack_seq));

Why Random ISNs?

If ISNs started at 0 every time, an attacker on the same network could predict the sequence number and inject forged segments into your connection. Random ISNs make this impractical — a classic example of security through unpredictability rather than secrecy.

Modern kernels also add a timestamp component to ISN generation to prevent sequence number wrapping on fast connections.

The Half-Open Problem

What happens if the SYN-ACK never arrives? The client retransmits the SYN up to tcp_syn_retries times (default 6 on Linux), then gives up. The server, meanwhile, holds a half-open connection in its SYN queue — this is exactly what SYN flood attacks exploit.

Next time I’ll look at how Linux’s SYN cookies defend against this without keeping per-connection state.