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