What congestion is
Every link has a finite rate. A router or switch keeps a buffer, a queue, in front of each outgoing link so it can absorb short bursts: packets wait their turn and leave as fast as the link allows. When packets arrive faster than they leave for long enough, the queue grows and every packet in it waits longer. When the queue is full, new arrivals are dropped.
That is congestion. The link has not failed, it is oversubscribed. People experience it as pages that load slowly, video that stalls, voice that breaks up, and requests that time out. A cell sector in a crowd, a shared Wi-Fi access point, and a busy uplink from a building can all congest in the same way.
Congestion collapse
Delay and loss are the network’s way of telling senders to slow down. The danger comes when senders do the opposite.
RFC 896, written by John Nagle in 1984, described what happened on early IP networks. As switching nodes became congested, round-trip times rose. When a host’s round-trip time exceeded its maximum retransmission interval, the host began putting more copies of the same datagrams into the network. Buffers filled, packets were dropped, and in the end every packet was being sent several times, with throughput reduced to a small fraction of normal. Nagle noted that this state was stable: the network stayed in it.
RFC 2914, the IETF’s statement of congestion control principles, defines congestion collapse informally as the point where an increase in network load results in a decrease in the useful work done by the network. It credits the congestion avoidance mechanisms Van Jacobson developed from 1986 onwards with preventing it in TCP. It also names a second form: bandwidth spent carrying packets that are dropped before they reach their destination, a risk from applications that send without any end-to-end congestion control.
How senders back off
TCP’s congestion control, specified in RFC 5681, keeps a congestion window: a limit on how much unacknowledged data the sender may have in flight. Four algorithms manage it:
- Slow start grows the window quickly from a small initial value as acknowledgments arrive.
- Congestion avoidance grows it by roughly one full-sized segment per round trip once the sender is near the path’s capacity.
- Fast retransmit resends a segment that appears lost when the third duplicate acknowledgment arrives, without waiting for a timer. On that signal the sender sets its slow start threshold to half the data in flight, but no less than two segments.
- Fast recovery keeps data flowing at the reduced rate after that retransmission instead of starting over from a small window.
The pattern is to probe gently for capacity and to cut back sharply on loss. CUBIC, specified in RFC 9438, keeps that pattern but uses a cubic function rather than a linear one to grow the window, to scale better on fast, long-distance paths. The RFC states that CUBIC has been adopted as the default TCP congestion control algorithm by the Linux, Windows, and Apple stacks. For QUIC, RFC 9002 specifies loss detection and a sender-side congestion controller similar to TCP NewReno.
What routers do about it
The traditional router behaviour is tail drop: accept packets until the queue is full, then drop whatever arrives next. RFC 7567, an IETF Best Current Practice on queue management, lists its drawbacks. Queues stay full for long periods, adding delay to everything. A few flows can monopolise the queue and lock others out. Bursts arriving at a full queue lose several packets from the same flow, and flows sharing a bottleneck can fall into step, backing off and speeding up together.
The recommended fix is active queue management (AQM): the router drops or marks packets with Explicit Congestion Notification before the queue is full, so senders respond before buffers overflow. RFC 7567 points out the counterintuitive result that queues that are normally small can give higher throughput as well as lower delay, and that early signalling reduces drops only while traffic is dominated by senders that respond to it.
What this means for apps
An app cannot fix a congested network, but it can avoid making it worse and avoid misreading it:
- Use a congestion-controlled transport. TCP and QUIC back off on their own. An app that sends its own UDP traffic takes on that responsibility.
- Space out retries. App-level retries are extra load on a path that is already full. Use exponential backoff with jitter and a cap on attempts, so clients that failed together do not retry together.
- Treat slow as slow, not down. A timeout under congestion does not mean the server is gone. Keep the user’s work locally and send it when the path clears, as in graceful degradation.
- Watch the multiplier in a mesh. Where devices share one radio channel and relay for each other, every forwarded copy uses airtime. Flooding multiplies traffic per message, which is why hop limits and duplicate suppression matter as a mesh grows.