A TCP connection, packet by packet.
Every web page, every API call, every Slack message rides on a TCP connection. They all follow the same shape: a three-way handshake to open, data exchanged with sequence numbers and acknowledgements, a four-way teardown to close. Watch one happen — twelve packets in total — with every header field labelled, both sides' state machines visible, and what each flag actually means.
curl http://10.0.2.10/ (port 80) Client at 10.0.1.5 → Server at 10.0.2.10:80 · open · GET · response · closeServer is bound to port 80 and LISTENing. Client hasn't connected yet — its socket is CLOSED. The server already has its initial sequence number picked (4500); the client has its (1000) ready for when it dials.
- Three-way handshake
- SYN → SYN-ACK → ACK. Three packets to agree both sides are alive and pick starting sequence numbers.
- Sequence number
- A counter each side keeps for the bytes it has sent. The other side ACKs the next byte it expects.
- Port
- A 16-bit number identifying a connection endpoint on a host. Multiple connections share one IP via different ports.
Why it's three packets, not one
A single packet would have to carry both sides' initial sequence numbers and prove both can hear each other. It can't. The three-way handshake is the minimum: client says "I want to talk, my seq starts at X." Server says "OK, I can hear you (ACK X+1), and my seq starts at Y." Client says "Got it (ACK Y+1)." Both sides know both can speak and both can hear, with starting numbers locked in.
The teardown takes four because each direction closes independently — you can be done sending but still want to receive, so each side FINs separately and waits for the ACK.
What this simplifies
- No TLS. A real HTTPS connection runs another handshake on top of this — usually 1 more RTT for TLS 1.3, 2 for 1.2.
- No congestion control. The packets all arrive in order, no loss, no retransmits. Real TCP has slow start, congestion avoidance, fast retransmit, SACK.
- No window scaling. Both sides advertise a receive window so the sender knows how much to keep in flight. We omit it.
- No Nagle / delayed ACKs. Optimisations that bunch small sends and ACKs to avoid overhead.
- No fragmentation. The 200-byte response fits in one segment. Real ones often don't and get split across MSS-sized packets.
Why this matters in production
Every HTTPS request to your server pays the handshake cost twice — once for TCP, once for TLS. That's ~3 RTTs before any HTTP byte moves. Across a continent, ~100 ms. Across an ocean, ~300 ms. This is the reason connection pooling and HTTP/2 multiplexing exist: open the connection once, reuse it forever.
The teardown matters too. TIME_WAIT holds the port for 60 seconds after close. A busy server doing thousands of short connections per second can run out of ephemeral ports if it always initiates the close. The cure is to make the client close instead, or to enable SO_REUSEADDR.
TCP, the full chapter →
Congestion control, retransmission, SACK, window scaling, Nagle, the modern variants — Reno, Cubic, BBR.
Open the Codex