KomuraSoft LLC
Chapter 5

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.

Graph-style diagram of cwnd rising exponentially in slow start, switching to linear plus-one growth past the threshold, dropping to half or to one on loss detection, and climbing again

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.

Slow start — double every RTT
Start from cwnd = 1 MSS (one segment) and grow on every ACK that returns safely. The result: 1 → 2 → 4 → 8 → 16… doubling each RTT. Despite the name, it is exponential rapid acceleration
Congestion avoidance — +1 per RTT
Past the threshold (ssthresh: the remembered level where loss last struck), switch to addition, 1 MSS per RTT. Feel gently around the limit

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.

3 duplicate ACKs (mild)
Later data is arriving = the road is alive. Cut cwnd roughly in half and continue in congestion avoidance
Timeout (severe)
Nothing is coming back = a serious blockage. Reset cwnd to 1 MSS and start over from slow start

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?

Q2. Why does TCP use packet loss as its signal of congestion?

Q3. What determines how much data the sender may have outstanding (the in-flight limit) at any moment?

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?

MSS

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?

MSS

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.)

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.