TCP congestion control

Advanced

If every sender sent as fast as it could, router queues would overflow, packets would be dropped, and senders would resend them, making things worse: congestion collapse, which nearly stopped the internet in 1986. Since then, every TCP sender slows down when the network looks busy.

TCP has no message from the network saying "busy". It infers congestion from loss: a timeout, or three duplicate ACKs.

The congestion window

The sender may have at most cwnd (congestion window) segments in flight; the window from the last lesson is the smaller of cwnd and the receiver's buffer (flow control). cwnd grows while all is well and shrinks after a loss.

  • Slow start: cwnd starts at 1 segment and grows by 1 for every ACK, so it doubles every round trip, until it reaches the threshold ssthresh. ("Slow" only compared with sending everything at once.)
  • Congestion avoidance: above ssthresh, grow by just 1 segment per round trip.
  • On a timeout: a serious sign. Set ssthresh to half of cwnd, set cwnd back to 1, and slow-start again.
  • On three duplicate ACKs: later segments are still arriving, so things are less bad. TCP Reno halves cwnd (ssthresh = cwnd = half) and continues with congestion avoidance: fast recovery. The older TCP Tahoe treats it like a timeout.

Growing by adding 1 and shrinking by halving is called AIMD (additive increase, multiplicative decrease). It gives the sawtooth below, and it makes senders sharing one bottleneck converge to a fair share each.

cwnd, ssthresh = 1, 16
losses = {9: "3 dup ACKs"}           # in round 9, three duplicate ACKs arrive

for rnd in range(1, 13):
    print("round", rnd, "cwnd", cwnd, "ssthresh", ssthresh)
    if rnd in losses:
        ssthresh = max(cwnd // 2, 2)
        cwnd = ssthresh              # Reno: halve and carry on
    elif cwnd < ssthresh:
        cwnd = min(cwnd * 2, ssthresh)   # slow start
    else:
        cwnd += 1                    # congestion avoidance
Open in compiler

Today

Linux, Android and Windows use CUBIC, which grows the window along a cubic curve so it recovers faster on fast, long links. Google's BBR measures the bottleneck rate and round-trip time directly instead of waiting for losses. Both still back off when the network is congested.

Common mistakes

  • Mixing up flow control (do not overflow the receiver) and congestion control (do not overflow the network).
  • Thinking slow start grows by 1 per round trip. It doubles; congestion avoidance adds 1.
  • Forgetting that ssthresh is set to half of cwnd at the moment of the loss, before cwnd changes.

Quick check

1. During slow start, cwnd every round trip:
2. cwnd = 20 when a timeout happens. What are ssthresh and cwnd after it?
3. Reno sees three duplicate ACKs at cwnd = 16. What is cwnd next?
4. What does congestion control protect?

Practice problems for this lesson

My notes

Write down what you want to remember from this lesson. Notes are saved on this device as you type, and appear on your dashboard.