The second window: how fast may I send?
In guide 1 you met the receive window — the receiver's promise of how much it can hold. In guide 2 you met the AIMD idea — the social contract that keeps the Internet from melting. Now we install that contract as an actual gear inside the sender. TCP keeps a private number called the congestion window, usually written cwnd. It is the sender's own guess of how much unacknowledged data the network — not the receiver — can safely swallow right now. The receiver never sees it; it lives entirely on the sending side.
So a TCP sender is held back by two limits at once, and it must obey the smaller of them. It may have at most min(cwnd, receive window) bytes in flight — unacknowledged — at any moment. Think of two valves on the same pipe: the receive window is how wide the receiver's bucket allows, the congestion window is how wide the network allows, and water flows at whichever valve is more closed. This guide is entirely about the second valve: how TCP sets cwnd when nobody tells it the network's true capacity.
Slow start: not slow, but a fast guess from nothing
When a connection is brand new, the sender knows nothing about the path. Blasting a huge burst could instantly overflow some router and trigger the congestion collapse you met in guide 2. So TCP starts tiny — a cwnd of a few segments — and ramps up. The phase is called slow start, which is a misleading name: it actually grows the window exponentially. The rule is beautifully simple. Every time an ACK confirms a segment, increase cwnd by roughly one segment. Because a whole window's worth of ACKs comes back each round trip, the window doubles every round-trip time.
- Round 0: cwnd = 1 segment. Send 1 segment, wait one RTT for its ACK.
- Round 1: that 1 ACK bumped cwnd to 2. Send 2, get 2 ACKs back.
- Round 2: cwnd = 4. Round 3: cwnd = 8. Round 4: cwnd = 16 — doubling each RTT.
- This climb continues until cwnd reaches a threshold (called ssthresh) or a loss happens — at which point the rules change, as we'll see next.
Why exponential, if the goal is caution? Because the unknown capacity might be enormous, and creeping up one segment per round trip would waste minutes of a fast link sitting half-empty. Doubling reaches the right neighborhood in only a handful of round trips — to get to a window of 64 segments takes about 6 RTTs, not 64. Slow start is really fast probing: get into the ballpark quickly, then hand off to a gentler phase before you overshoot and crash.
Congestion avoidance: the careful linear climb
Doubling forever would itself cause a crash — you'd overshoot the path's capacity within a single round trip. So once cwnd crosses the threshold ssthresh, TCP switches gears into congestion avoidance. Now growth is linear, not exponential: cwnd rises by about one segment per round trip, no matter how many ACKs arrived. This is the Additive Increase of AIMD made concrete — the gentle, patient acceleration of a careful driver who is already near the speed limit and only nudges the pedal.
The two phases share one purpose but differ in nerve. Slow start says "I have no idea how fast I can go, so let me find out quickly." Congestion avoidance says "I'm probably near the limit now, so let me press only a little harder each trip and watch for the first sign of trouble." The first sign of trouble is loss — a missing ACK or, faster, the duplicate-ACK pattern that triggers fast retransmit (we open that up properly in guide 4). When loss appears, the Multiplicative Decrease half of AIMD fires: cwnd is cut sharply — roughly halved — and the climb begins again from there.
The sawtooth: what AIMD looks like over time
Now put the pieces on a graph: time across the bottom, cwnd up the side. Linear increase draws a slow upward ramp; a loss draws a sudden vertical drop to about half; then the ramp starts again. Repeat, and you get a row of slanted teeth — the famous TCP sawtooth. It is not a sign that something is broken. It is exactly what healthy congestion control is supposed to look like: TCP is forever gently probing for more bandwidth, finding the ceiling, backing off, and probing again. The connection lives in a productive cycle of pushing and yielding.
cwnd
| /| /| /|
| / | / | / |
| / | / | / |
| / v / v / v <- loss: cut ~in half
| / \ / \ /
| / \/ \/
| / slow start linear climb (congestion avoidance)
|/_____exponential__________________________________ time
(doubles each RTT, until ssthresh or first loss)The sawtooth also quietly explains fairness, a thread guide 5 will pull on. Two flows sharing one bottleneck both grow by one segment per RTT (the same additive step) but both cut by the same fraction on loss (the same multiplicative step). The math of "add the same, subtract a fraction" nudges unequal flows toward an equal share over time. No central referee assigns the split — it emerges from every sender independently obeying the same simple rule. That's the quiet genius of AIMD made visible in the teeth.
Honest limits: when the sawtooth is the wrong shape
The classic sawtooth we just drew belongs to TCP Reno, the textbook loss-based scheme. It works, but it has real weaknesses on a modern Internet, and being honest about them is the whole point of mastery. On a fat, long-distance link — high bandwidth-delay product — Reno's one-segment-per-RTT climb is painfully slow to refill the pipe after each halving, so it can leave a fast path badly underused. This is why CUBIC, today's common default on Linux and Android, replaces the straight ramp with a curve that shoots back up toward the previous ceiling quickly, then eases off as it nears it.
There is a deeper assumption to question. The whole sawtooth rests on reading loss as the signal of congestion — and that misfires twice. On Wi-Fi or cellular, a packet often dies to radio interference, not a full queue, yet loss-based TCP backs off anyway and throughput sags (you saw this caution in the transport rung). And on the flip side, today's oversized router buffers can soak up huge backlogs without dropping anything, so loss arrives late while delay quietly balloons — the bufferbloat problem guide 5 tackles head-on. Newer designs like BBR try to sidestep both by modeling the path's bandwidth and round-trip delay directly instead of waiting for a drop.
Keep the layering honest, too. Slow start, congestion avoidance, and the sawtooth are sender-side behaviors built entirely from ordinary ACKs — TCP infers a verdict about the whole path from the trickle of confirmations coming back. The network never explicitly says "slow down" in classic TCP. Guide 5 introduces the modern exception, where a router can mark a packet to say exactly that, and guide 4 zooms into the loss-detection details — timeouts and fast retransmit — that decide the precise moment each tooth of the sawtooth turns down.