hop 23 of 32 · 6 min
Not overwhelming the network
Flow control protects the receiver. Nothing so far protects the routers in between, and in 1986 the internet found out what happens: throughput collapsed by a factor of a thousand as everyone retransmitted into already full queues.
The signal
There is no message saying "the network is congested". TCP infers it from packet loss, on the reasoning that wired links rarely corrupt packets, so a loss almost certainly means a router queue overflowed.
That inference is wrong on wireless, where loss is often interference. TCP responds by slowing down when it should retransmit, which is why TCP over a lossy wireless link underperforms.
Slow start
Begin with a small window and double it every round trip.
round trip 1: 2 segments
round trip 2: 4
round trip 3: 8
round trip 4: 16Exponential, despite the name, which refers to starting small rather than growing slowly. It probes for the available capacity quickly without assuming any.
Congestion avoidance
Past a threshold, growth becomes linear: one segment per round trip. The protocol is now near the capacity it believes exists and stops doubling.
On loss
Three duplicate acknowledgements: one segment is missing but later ones are arriving, so the path still works. Halve the window and continue. This is fast recovery.
Timeout: nothing is arriving. Something is badly wrong. Drop to the minimum window and slow start again.
window
| /\ /\ /\ the sawtooth
| / \ / \ / \
| / \__/ \__/ \
| /
+------------------------------ timeAdditive increase, multiplicative decrease. The sawtooth is what a healthy TCP connection looks like, and it is what makes many connections converge on a roughly fair share of a link without any coordination.
Why halving rather than stopping
Halving backs off enough to drain the queue while keeping the connection productive. Stopping would be slow to recover and would leave capacity unused; a small reduction would not relieve the congestion. The multiplicative decrease is what makes it stable when many connections share a link.
Newer algorithms
CUBIC, the Linux default, grows more aggressively after a loss so that long fast links recover quickly.
BBR, from Google, estimates bandwidth and round trip directly instead of waiting for loss. This matters because of bufferbloat: oversized router queues mean packets are delayed rather than dropped, so loss based control keeps increasing until latency is enormous. BBR targets the point where throughput stops improving, before queues fill.