Congestion control — TCP yields to network congestion
Nobody tells you how busy the path is. TCP grows its own window, cwnd, by doubling, and yields when packet loss signals congestion — follow this probe-as-you-send scheme by hand.
The peer is fine, but the road is jammed
Chapter 4's flow control ensures we no longer drown the peer. So may we blast away at full speed as long as rwnd allows? No. Between sender and receiver lies a shared network that other people's traffic pours into as well. Whatever exceeds a router's capacity piles up in its queue, and when the queue overflows, packets are dropped. If everyone sent at full speed, retransmissions would breed retransmissions until the whole network seized up and nobody's data flowed (congestion collapse — an accident that actually happened in the 1980s).
The awkward part: nobody tells you the path's spare capacity. There is no declaration like rwnd. So as the second half of promise 3, TCP carries congestion control, in which the sender estimates the path capacity on its own. The estimate lives in another window — the congestion window (cwnd) — and the amount actually allowed to be outstanding is min(rwnd, cwnd). The peer's circumstances and the road's circumstances: adapt to whichever is stricter.
The life of cwnd. Race upward by doubling (slow start), step one at a time past the threshold (congestion avoidance), yield on loss, and climb again. The jagged sawtooth is TCP healthily probing the limit, over and over.
How to probe — race by doubling, close in by steps
On a road of unknown capacity, the only option is to start small and increase while watching. TCP probes with a two-speed gearbox.
Exceed the path's limit and packet loss occurs, triggering Chapter 3's detection. Here TCP shows its wisdom: the punishment matches the weight of the evidence.
Grow, collide, yield, grow again. The sawtooth wave that cwnd traces is something like the greeting TCP keeps paying so that everyone can share the network's capacity. Had it been designed so that nobody yields, the internet would not have held together.
Practice 5-1 — Telling the two brakes apart
Learn to distinguish the two windows by asking whose circumstances each one adapts to.
Q1. Which correctly describes the difference between flow control and congestion control?
They adapt to different parties. Flow control (Chapter 4's rwnd) follows the buffer space that the receiver declares. Congestion control (this chapter's cwnd) is the sender's own estimate of path congestion that nobody declares. The receiver can be healthy while the network is jammed, and vice versa, so two independent brakes are required.
Q2. Why does TCP use packet loss as its signal of congestion?
Routers on the path fundamentally tell you nothing (explicit notification mechanisms exist, but cannot always be relied on). Yet as a physical consequence of congestion, an overflowing queue drops packets. Hence the inference 'loss occurred = some queue is close to overflowing' holds. Loss also has non-congestion causes such as radio quality, so it is not a perfect signal — but it is the most universal one available without anyone's cooperation.
Q3. What determines how much data the sender may have outstanding (the in-flight limit) at any moment?
The two brakes act simultaneously. Between the peer's buffer (rwnd) and the estimated path capacity (cwnd), the stricter one — min(rwnd, cwnd) — is the actual limit. The 'window' in Chapter 4's ceiling = window ÷ RTT is, precisely speaking, this min. Raising only one of the two does not raise throughput if the other stays small.
How it shows up in practice
Congestion control is normally invisible, but it always surfaces in performance problems.
- Everyone is slow right after connecting — however fat the line, probing starts near cwnd = 1 MSS, so short transfers never reach top speed even once. This is why reusing one connection (keep-alive) beats repeatedly opening new ones for small requests.
- One loss costs far more than one retransmission — cwnd falls to half (or to 1), and recovery takes many RTTs. A 1% loss rate does not mean '1% slower'; it can mean slower by orders of magnitude.
- A throughput plateau has three suspects — rwnd (the peer's buffer), cwnd (path congestion), and the bandwidth itself. Plug them into Chapter 4's formula and ask which window is taking the min: that is the standard triage pattern.
Practice 5-2 — Hand-calculating cwnd growth
cwnd is counted in segments (units of MSS). Work out both kinds of growth: doubling and adding.
Q4. Transmission starts in slow start with cwnd = 1 MSS. If cwnd doubles every RTT, what is cwnd, in MSS, after 3 RTTs?
1 → 2 → 4 → 8, so after 3 RTTs it is 8 MSS. Despite the name 'slow,' the growth is exponential — increasing cwnd by 1 for every ACK of a segment sent in that RTT means it doubles every RTT. In around 10 RTTs it would reach 1024 MSS: fast enough to fill an idle line almost immediately.
Q5. Past the threshold (ssthresh), the connection is in congestion avoidance, growing from cwnd = 10 MSS by 1 MSS per RTT. What is cwnd, in MSS, after 5 RTTs?
10 + 5 = 15 MSS. If you kept doubling after learning the limit is near, the moment you overshoot you cause a burst of losses. So in the danger zone TCP switches to addition (+1 MSS per RTT) and feels for the limit gently. Race by doubling while far away; step carefully near the limit — this two-speed gearbox is how TCP probes.
Q6. TCP's reaction to detected loss differs by how the loss was detected. Which combination is correct? (Recall the two detection methods from Chapter 3.)
The punishment is matched to the weight of the evidence. Three duplicate ACKs (fast retransmit) mean 'later segments are arriving = the path is alive' — a mild symptom — so cwnd is roughly halved and the connection keeps running. A timeout means 'nothing is coming back' — a severe symptom — so cwnd drops to 1 MSS and slow start begins again. When you see a throughput cliff in a capture, ask which detection pulled the trigger — that is the reading used in Chapter 7.
Key takeaways from this chapter
- Flow control adapts to the peer; congestion control adapts to the network. The sending allowance is min(rwnd, cwnd)
- Path capacity is never declared, so TCP probes on its own, using packet loss as the congestion signal
- Growth has two gears — slow start doubles every RTT; congestion avoidance adds 1 MSS per RTT
- Punishment has two grades too — halve on 3 duplicate ACKs; reset to 1 and restart on timeout. The sawtooth wave is healthy mutual yielding
All three promises are now in place. The next chapter shifts the viewpoint to the application side. TCP keeps its promises perfectly, yet application data appears to 'go missing' or 'stick together' — the most common misunderstanding of TCP in practice: byte streams and framing.