Explain how TCP congestion control works. Cover slow start, congestion avoidance, and how TCP reacts differently to a timeout versus three duplicate ACKs. How is congestion control different from flow control?
Show hints
Track two variables: the congestion window (cwnd) and the slow-start threshold (ssthresh).
Show the explanation
Model answer. Congestion control stops senders from overwhelming the network. The sender keeps a congestion window (cwnd); the amount of unacknowledged data in flight is limited to min(cwnd, receiver's advertised window). The classic algorithm (TCP Reno) works as follows.
Slow start
- A new connection starts with a small cwnd (classically 1 MSS; modern stacks commonly use 10 segments per RFC 6928).
- cwnd grows by 1 MSS for every ACK received, so it roughly doubles every round-trip time — exponential growth, despite the name.
- This continues until cwnd reaches the slow-start threshold (ssthresh) or loss occurs.
Congestion avoidance (additive increase)
- Above ssthresh, cwnd grows by about 1 MSS per RTT — linear probing for spare capacity.
Reaction to loss (multiplicative decrease)
- Three duplicate ACKs: the receiver is still getting later segments, so the network is only mildly congested. TCP does fast retransmit of the missing segment without waiting for a timeout, sets ssthresh = cwnd / 2, and with fast recovery continues from about the halved window (Reno sets cwnd = ssthresh + 3 MSS during recovery, then ssthresh once new data is acknowledged) instead of restarting slow start.
- Retransmission timeout (RTO): nothing is getting through, a sign of severe congestion. TCP sets ssthresh = cwnd / 2, resets cwnd to 1 MSS, and re-enters slow start.
This "additive increase, multiplicative decrease" (AIMD) gives the familiar sawtooth cwnd graph and moves competing flows towards a fair share.
Congestion control vs flow control
- Flow control protects the receiver: the receiver advertises its free buffer space in the TCP window field (rwnd), and the sender never exceeds it.
- Congestion control protects the network: cwnd is the sender's own estimate of what the path can carry; it is never sent in a header.
Modern variants: CUBIC (Linux default) grows cwnd as a cubic function of time since the last loss; BBR models bottleneck bandwidth and RTT instead of relying mainly on loss.
Follow-up: "Why is loss a poor congestion signal on Wi-Fi?" Radio errors cause losses too, so loss-based TCP slows down unnecessarily.
Original UniCircle question. Last reviewed 28 Oct 2026.
