RFC 2581
SUPERSEDEDTCP Congestion Control
M. Allman, V. Paxson, W. Stevens · April 1999 · original status: Proposed Standard · current status: Obsoleted (Proposed Standard, historic interest)
obsoleted by: RFC 5681 · updated by: RFC 3390
Obsoleted by RFC 5681 (2009): same four algorithms, refined — larger initial windows (with RFC 3390), byte-counted cwnd growth, clarified recovery details. SubnetLayer's engine implements the RFC 5681 subset; 2581 differences are taught as history.
This module teaches BOTH the historical problem and today's rules
This document
- RFC 2581 §3.1 — slow start and congestion avoidance
- RFC 2581 §3.2 — fast retransmit / fast recovery
- RFC 2581 §4.1 — restart after idle
Where the current rules live
- RFC 5681 §3.1–3.2 — the current versions of the same algorithms
TCP Congestion Control (2581 → 5681)
New to these ideas? Switch to the beginner level for a from-zero concept primer.
The problem this document answered
Four algorithms, one document: slow start, congestion avoidance, fast retransmit and fast recovery. The sender keeps cwnd — its own estimate of what the network can carry — and may have min(cwnd, rwnd) unacknowledged. cwnd grows exponentially, then linearly, and halves on loss: additive increase, multiplicative decrease.
Historical context
Jacobson's 1988 algorithms circulated as code and papers for a decade before RFC 2581 (April 1999) standardised them. It was updated by RFC 3390 (larger initial windows) and obsoleted by RFC 5681 (September 2009), which remains the current specification.
Current practice
RFC 5681 is the standard; Linux defaults to CUBIC and some stacks use BBR, but both keep the cwnd/ssthresh architecture and the AIMD skeleton. Initial windows today are typically 10 segments (RFC 6928), not the 1–4 of 1999.
Interactive laboratory
Guided walkthroughs — coached, packet by packet
Auto-pausing checkpoints explain every key packet: why it was sent, what each side believes, and what must happen next. Start here, explore freely after.
🎓 congestion collapseAuto-pausing checkpoints explain every key packet: why it was sent, what each side believes, and what must happen next. Start here, explore freely after.
Watch it on the wire
A client uploads a 21.9 KB file. Watch cwnd double each round trip, one segment get lost, three duplicate ACKs trigger a fast retransmit, and the window halve.
cwnd has grown large but the receiver only advertises 4 KB. The sender may use min(cwnd, rwnd) — so flow control, not congestion control, is the brake here.
A burst grows cwnd nicely, then the application goes quiet for eight seconds. When it speaks again the ACK clock is long gone — so cwnd restarts at the initial window rather than trusting stale evidence.
A slow link (60 B/ms), a 4-packet router queue, and a sender whose RTO (80 ms) is shorter than the queue-inflated RTT: premature retransmissions flood the queue with duplicates, drops multiply, and goodput falls while total traffic rises.
Compare correct vs historic / naive behaviour
Original RFC vs current standard
Initial window
- Original behaviour
- RFC 2581: initial cwnd of 1–2 segments (RFC 3390 later allowed ~4).
- Current behaviour
- RFC 5681 permits up to 4 segments; RFC 6928 (experimental, widely deployed) allows 10.
- What changed
- The starting burst grew roughly tenfold.
- Why it changed
- Most web flows are short and finish during slow start — a bigger initial window removes whole round trips from typical page loads.
📷 In a capture: Modern traces open with ~10 back-to-back segments; 1999-era traces open with one or two.
cwnd growth accounting
- Original behaviour
- RFC 2581 allowed incrementing cwnd per ACK received, which over-inflates under ACK division or stretch ACKs.
- Current behaviour
- RFC 5681 counts BYTES acknowledged, capped per ACK at one SMSS.
- What changed
- Growth is measured in acknowledged data, not in ACK packets.
- Why it changed
- A receiver splitting ACKs could otherwise inflate the sender's window far beyond what actually arrived.
📷 In a capture: Hard to see directly; the symptom of the old rule was a sender accelerating faster than delivered data justified.
Common misconceptions
- ✗“cwnd is negotiated with the receiver” — it is never transmitted; the peer cannot see it and does not consent to it.
- ✗“Congestion control replaces flow control” — both apply simultaneously; the send limit is the MINIMUM of cwnd and rwnd.
- ✗“Slow start is slow” — it doubles every RTT; it is only slow compared to the pre-1988 free-for-all.
- ✗“Loss always means congestion” — on wireless links it may mean corruption, which is why loss-based control underperforms there (and why BBR measures rate instead).
- ✗“A long-idle connection can resume at full speed” — RFC 5681 §4.1 says restart, because the old cwnd describes a network you no longer have evidence about.
Glossary terms used here
Knowledge check
1/3 · In the rwnd-limited scenario, cwnd has grown to ~14 KB while the receiver advertises 4 KB. What limits the sender?
2/3 · Why does RFC 5681 §4.1 reset cwnd after a long idle period?
3/3 · Loss detected by three duplicate ACKs halves ssthresh; loss detected by timeout collapses cwnd to one segment. Why the asymmetry?