Almost every byte you have ever read on the web arrived by TCP. When you loaded this page, two computers
performed a small, precise ceremony — a handshake to agree where to start counting, a stream of
numbered bytes flowing one way and acknowledgements flowing back, timers quietly watching for silence,
and finally a graceful goodbye. TCP is the workhorse of the internet, and having built up
This page dissects TCP as it actually runs: the fields in its header, the three-way handshake and why
it takes three messages rather than two, how a connection is torn down (and the curious
TIME_WAIT pause at the end), the way ACKs count bytes, and how TCP measures the
round trip so it knows how long to wait before assuming a packet is lost.
A TCP segment is a header (20 bytes without options) followed by application data. The two headline fields are the sequence number and the acknowledgement number, and the single most important thing to know about them is that they count bytes, not packets.
Alongside them sit the control flags — single bits that change a segment's meaning:
| Flag | Meaning |
|---|---|
| SYN | Synchronise — open a connection, carrying the initial sequence number |
| ACK | The acknowledgement number field is valid (set on nearly every segment after the first) |
| FIN | Finish — this side has no more data to send; begin an orderly close |
| RST | Reset — abort the connection abruptly (e.g. segment for a connection that doesn't exist) |
The header also carries the source and destination ports (the demux
Before any data flows, the two sides must agree on their starting sequence numbers. Each side chooses a random initial sequence number (ISN) and must tell the other what it is and confirm it heard the other's. That mutual agreement is what the three messages accomplish:
Why not two? Because each direction of the connection needs its sequence number acknowledged, and a two-message exchange can only confirm one direction. The third message is the client confirming it received the server's ISN. Two messages would leave the server unsure whether the client ever heard its chosen starting point — and, worse, would make the server vulnerable to old, duplicate SYNs: a stray SYN from a long-dead connection could trick a two-way server into opening a phantom connection. The three-way exchange lets each side confirm the other is genuinely present and listening now, defeating those delayed duplicates.
Closing is a four-way affair because each direction closes independently — TCP
connections are full-duplex, so each side sends its own FIN and gets its own
ACK. One side can finish sending (FIN) while still receiving, a "half-close." Once both
FINs are acknowledged, the connection is done — almost. The side that closed first (usually the
client) enters a state called TIME_WAIT and lingers there for roughly
Operators see thousands of sockets stuck in TIME_WAIT and panic — "my connections aren't
closing, something's leaking!" — and reach for hacks to kill it. That is a mistake.
TIME_WAIT is doing exactly its job, and it exists for two solid reasons:
TIME_WAIT can this side still be
around to re-ACK it; leave too early and the peer is stranded, unable to close cleanly.
So TIME_WAIT is a correctness feature, not a leak. It is the side that initiated
the close that pays the cost — which is one reason high-throughput servers prefer to let
clients close first, so the TIME_WAIT burden lands on the many clients rather
than the one server.
TCP's retransmission depends on a timeout, but what value? Too short and it retransmits needlessly; too long and it dawdles after a real loss. And the right value changes constantly — the RTT to a server varies with congestion and route. So TCP measures it continuously and adapts.
Each time a segment is acknowledged, TCP takes a SampleRTT (how long that segment took to be ACKed) and folds it into a smoothed running average — an exponentially weighted moving average (EWMA):
It also tracks how much the samples jitter — the deviation — because a variable link needs a bigger safety margin than a steady one:
The retransmission timeout is the estimate plus a generous margin proportional to that jitter:
The factor of four means a bursty, jittery path gets a comfortably longer timeout, avoiding spurious retransmissions, while a rock-steady path gets a tight one that reacts to real loss quickly. The demo runs the EWMA over a few noisy samples:
There's a subtle trap in measuring RTT. Suppose you send a segment, time out, retransmit, and then an ACK arrives. Was that ACK for the original or the retransmission? You can't tell — so you can't know the true SampleRTT. Karn's algorithm says: simply don't take an RTT sample from any retransmitted segment. To stay safe after a loss, TCP also doubles the RTO on each timeout (exponential backoff) until a clean, non-retransmitted ACK gives it a fresh sample to reset from. Ambiguous measurements are worse than no measurement.