§3.7.1Classic TCP Congestion Control

Transport layer Kurose & Ross pp. 263–270 · ~15 min read

  • congestion window
  • loss event
  • self-clocking
  • bandwidth probing
  • slow start
  • slow-start threshold
  • congestion avoidance
  • fast recovery
  • tcp tahoe
  • tcp reno
  • additive-increase multiplicative-decrease

Where you are

  • Application layer
  • Transport layer you are here
  • Network layer
  • Link layer
  • Physical layer

TCP (Transmission Control Protocol) has no idea what the network can carry, so it finds out the only way available to it. It speeds up until something breaks, backs off, and starts speeding up again.

Words you will meet

  • cwnd — the congestion window: the sender’s own limit on unacknowledged data.
  • ssthresh — the slow-start threshold: where doubling stops and creeping begins.
  • Loss event — a timeout or three duplicate acknowledgements.
  • Self-clocking — letting arriving acknowledgements set the pace of growth.
  • AIMD (additive-increase, multiplicative-decrease) — the rule that makes the saw tooth.

Why this matters

Section 3.6 established what congestion costs and showed that the Internet took the end-to-end road: no router tells TCP (Transmission Control Protocol) anything. Classic TCP — standardised in RFC (Request For Comments) 2581 and most recently RFC 5681 — must therefore work it out for itself.

Three questions organise everything below. How does a sender limit its rate? How does it know there is congestion? And what algorithm should it use to change the rate?

A misprint at the start of section 3.7

The opening paragraph says “We’ll first cover this ‘classic’ version of TCP in depth in Section 7.3.1. In Section 7.3.2, we’ll then look at newer flavors of TCP…”.

It means 3.7.1 and 3.7.2 — this section and the next. Chapter 7 is wireless and mobile networks. It is a leftover from an earlier edition’s numbering.

The window, and the rate

The answer to the first question is a variable. The sender keeps a congestion window , cwnd, and obeys

LastByteSentLastByteAckedmin{cwnd,rwnd}LastByteSent - LastByteAcked \le \min\{cwnd, rwnd\}

For this section, assume the receive buffer is large enough that rwnd never binds; section 3.5.5 covered the case where it does. Assume too that the sender always has data to send.

What a congestion window is worth

What each symbol means

  • cwndthe congestion window (bytes)
  • RTTround-trip time (s)

Read aloud: The sender may put one window of data into the connection each round trip, so the window divided by the round-trip time is roughly its rate.

Sending rate20.0 kbps

cwnd = 1 segment × 500 bytes = 500 bytes
one window may be outstanding per round trip
rate ≈ cwnd / RTT = 500 × 8 bits / 200 ms
                  = 20.0 kbps
this is where every TCP connection starts — one segment per round trip

Change any number above and the arithmetic re-runs, carrying the units through.

The defaults are the book’s own slow-start example. Then set MSS to 1460 and the round trip to 0.18 to see where Lin’s connection begins.

In plain words

At the start of each round trip the sender may put cwnd bytes into the connection, and by the end the acknowledgements are back. So its rate is roughly cwnd/RTT.

That is why the book can talk about adjusting the rate and adjusting the congestion window as though they were the same act. They are.

How it knows, and what it does about it

A loss event is a timeout, or the receipt of three duplicate acknowledgements — the two signals section 3.5.4 built. When a router buffer overflows, a segment is dropped, and one of those two things eventually happens at the sender.

And when nothing is lost, acknowledgements keep arriving. TCP takes that as evidence that all is well and increases the congestion window.

Self-clocking

Notice what paces the increase: the acknowledgements themselves.

If acknowledgements return slowly — a long path, or a slow link — the window grows slowly. If they return quickly, it grows quickly. TCP is self-clocking : it adapts its own rate of adaptation to the path, without measuring the path at all.

Three principles follow, and the book states them as such:

  1. A lost segment implies congestion, so the rate should be decreased.
  2. An acknowledged segment implies the network is delivering, so the rate can be increased.
  3. Bandwidth probing : increase in response to acknowledgements until a loss event, then back off, then start probing again.

Everyday picture

The book’s own: a child who asks for more and more sweets until finally told “No!”, backs off a little, and then starts asking again shortly afterwards.

Where the picture breaks: the child is told “no” by someone who knows the answer and says so immediately. TCP is told nothing at all — it infers the refusal from a segment that failed to arrive, long after the fact. And every other sender is asking at the same time, none of them coordinating.

Slow start

Every connection begins here. cwnd starts at 1 MSS (maximum segment size) , and increases by 1 MSS every time a transmitted segment is first acknowledged.

Figure 3.50 — slow start doubles every round trip
message 10 of 10
Host Acwnd starts at 1 MSSTimeHost BTimecwnd = 1 MSScwnd = 2 MSScwnd = 4 MSSone segmentACKsegmentsegmentACKACKsegmentsegmentsegmentsegment

Click any arrow to see what that message says and why it is sent.

One, then two, then four. The name describes where it starts, not how fast it grows.

Read this diagram as text
  1. Host A sends one segment to Host B. cwnd = 1 MSS. With MSS 500 bytes and a 200 ms round trip that is about 20 kbps — genuinely slow, and the sender has no idea yet whether the path could carry a thousand times more.
  2. Host B sends ACK to Host A.
  3. Host A sends segment to Host B. The acknowledgement increased cwnd by one MSS, so two segments now go out. cwnd = 2 MSS.
  4. Host A sends segment to Host B.
  5. Host B sends ACK to Host A.
  6. Host B sends ACK to Host A.
  7. Host A sends segment to Host B. Each of the two acknowledgements added one MSS, so cwnd is now 4 MSS and four segments go out. The rate doubles every round trip — exponential growth.
  8. Host A sends segment to Host B.
  9. Host A sends segment to Host B.
  10. Host A sends segment to Host B.

Lifelines, left to right: Host A (host), Host B (server).

One segment, then two, then four: the rate doubles every round trip. The send rate starts slow and grows exponentially — which is a slightly awkward thing for something called slow start , and the book says so.

Figure 3.51 also gives the other half of the starting state, which the prose does not: a fresh connection begins with ssthresh = 64 KB. That is deliberately large — it means a first connection will normally keep doubling until something actually goes wrong, rather than stopping at a threshold no measurement has justified yet.

Three ways slow start can end

  1. A timeout. cwnd goes back to 1 MSS and slow start begins again. It also sets ssthresh = cwnd/2 — half the window at the moment congestion was detected.
  2. cwnd reaches ssthresh. Since ssthresh is half of what caused trouble last time, it would be “a bit reckless to keep doubling”. TCP switches to congestion avoidance.
  3. Three duplicate acknowledgements. TCP performs a fast retransmit and enters fast recovery.

Congestion avoidance

On entry, cwnd is roughly half its value when congestion was last encountered — so congestion could be just around the corner. TCP therefore stops doubling and increases cwnd by a single MSS per round trip.

The usual implementation increases cwnd by MSS × (MSS/cwnd) on each new acknowledgement. With MSS 1,460 bytes and cwnd 14,600 bytes there are ten segments in flight, so each acknowledgement adds a tenth of an MSS. Once all ten are acknowledged, cwnd has grown by exactly one MSS.

Congestion avoidance ends the same two ways: a timeout, treated exactly as in slow start; or three duplicate acknowledgements, treated very differently.

Why the two loss signals are not treated alike

Three duplicate acknowledgements mean the network is still delivering segments — three later ones got through to generate them. A timeout means silence, which could be anything up to a broken path.

So TCP’s response to duplicates is deliberately less drastic. It halves cwnd, adds 3 MSS “for good measure to account for the triple duplicate ACKs received”, records ssthresh as half the congestion window, and enters fast recovery.

A timeout gets the full retreat: cwnd to 1 MSS.

Fast recovery

In fast recovery , cwnd is increased by 1 MSS for every further duplicate acknowledgement for the missing segment. Each one is evidence that another segment has left the network, so there is room to send another.

When an acknowledgement finally arrives for the missing segment, TCP deflates cwnd and enters congestion avoidance. If a timeout occurs instead, it goes to slow start with cwnd = 1 MSS, like everything else that times out.

Fast recovery is recommended but not required [RFC 5681] — and that optional status is the whole difference between two famous versions of TCP.

The three components, and what each does with a window
How cwnd growsHow it ends

Cells marked ⓘ have an explanation — click to read it. Sortable columns have a ↕ in the heading.

Slow start and congestion avoidance are mandatory. Fast recovery is recommended and not required — and skipping it is exactly what makes TCP Tahoe.

Tahoe and Reno

TCP Tahoe , the earlier version, unconditionally cut cwnd to 1 MSS and entered slow start after either kind of loss event. TCP Reno added fast recovery.

Figure 3.52 — Tahoe and Reno, same connection, same loss
0.0004.08.012.016.01.03.86.69.412.215.0transmission roundcongestion window (MSS)

At 15.0 (move the pointer over the plot to read it anywhere):

  • TCP Reno:15.0 MSS
  • TCP Tahoe:9.0 MSS
  • ssthresh:6.0 MSS

Identical for the first eight rounds. Then a triple-duplicate-ACK loss at cwnd = 12, and they part company completely. Drag the sliders to move the threshold or the loss.

Read the plot round by round

ssthresh starts at 8 MSS, and for the first eight rounds the two are identical.

  • Rounds 1–4: slow start, 1 → 2 → 4 → 8. At 8 it meets ssthresh, so doubling stops.
  • Rounds 5–8: congestion avoidance, 9 → 10 → 11 → 12, one per round.
  • Just after round 8: a triple-duplicate-ACK loss, with cwnd = 12 MSS.
  • Both set ssthresh = 0.5 × 12 = 6 MSS.
  • Reno sets cwnd = ssthresh + 3 = 9 MSS and grows linearly from there.
  • Tahoe sets cwnd = 1 MSS, grows exponentially back up to ssthresh = 6, and only then grows linearly.

Reno is back at 12 by round 12. Tahoe does not reach 12 until round 18.

The whole machine

Figure 3.51 — the whole of TCP congestion control
new ACKcwnd=cwnd+MSSdupACKcount=0transmit new segment(s), as allowedduplicate ACKdupACKcount++timeoutssthresh=cwnd/2cwnd=1 MSSdupACKcount=0retransmit missing segmentcwnd ≥ ssthreshΛnew ACKcwnd=cwnd+MSS·(MSS/cwnd)dupACKcount=0transmit new segment(s), as allowedduplicate ACKdupACKcount++timeoutssthresh=cwnd/2cwnd=1 MSSdupACKcount=0retransmit missing segmentdupACKcount==3ssthresh=cwnd/2cwnd=ssthresh+3·MSSretransmit missing segmentdupACKcount==3ssthresh=cwnd/2cwnd=ssthresh+3·MSSretransmit missing segmentduplicate ACKcwnd=cwnd+MSStransmit new segment(s), as allowedtimeoutssthresh=cwnd/2cwnd=1dupACKcount=0retransmit missing segmentnew ACKcwnd=ssthreshdupACKcount=0SlowstartCongestionavoidanceFastrecovery
Fire an event

Outlined buttons are the events this state can actually handle. Try the others too — what cannot happen is as much of the protocol as what can.

Three states and eleven transitions. Drive it: open in slow start, fire new ACKs until it crosses into congestion avoidance, then send it round the loss paths. The hardest figure in the chapter, and the most rewarding to operate.

In plain words

Three states, and every transition in TCP congestion control is one of eleven arrows.

Two things are worth noticing while you drive it. Every timeout leads to slow start, from wherever you are — that is the one uniform rule. And fast recovery has no self-transition on a new ACK: the first new acknowledgement ends it, deflating cwnd and moving to congestion avoidance.

Retrospective — the saw tooth

Step back from the details. Ignore the slow start at the beginning of a connection, and assume losses are signalled by triple duplicate acknowledgements rather than timeouts. What is left is:

  • linear increase of cwnd by 1 MSS per round trip, and
  • halving of cwnd on a loss event.

That is additive-increase, multiplicative-decrease AIMD.

Figure 3.53 — additive increase, multiplicative decrease
0.00010.020.030.040.01.012.824.636.448.260.0transmission roundcongestion window (MSS)

At 60.0 (move the pointer over the plot to read it anywhere):

  • cwnd:22.3 MSS

Slow start ignored, every loss signalled by duplicate acknowledgements. Up one per round trip, halved on loss, forever. This shape is TCP probing for bandwidth.

The shape is the argument

The saw tooth is bandwidth probing, drawn. TCP increases its congestion window until a loss tells it it has gone too far, cuts it in half, and immediately begins climbing again to see whether conditions have changed.

There is no signalling anywhere in this. Every sender acts on local information only, asynchronously from every other sender, and the result is a network that shares itself out tolerably well.

AIMD was arrived at by engineering insight and experimentation, not derived. Ten years after TCP was deployed, theoretical analysis showed that the algorithm acts as a distributed asynchronous-optimisation algorithm, simultaneously optimising several aspects of user and network performance [Kelly 1998]. A rich theory of congestion control has followed [Srikant 2012].

Check yourself

Check yourself

0 of 6 answered
  1. 1.Why is it called slow start when the window doubles every round trip?

  2. 2.predictOn the Figure 3.52 plot, at which round does the congestion window stop doubling and start adding one?

    Watch where the curve meets the ssthresh line.

  3. 3.predictThe loss at round 8 happens with cwnd = 12. Compare what Reno and Tahoe do next.

  4. 4.Why does TCP respond less drastically to three duplicate acknowledgements than to a timeout?

  5. 5.predictDrive the congestion-control machine into congestion avoidance and fire "a new ACK arrives". What happens to cwnd?

  6. 6.Ignoring slow start and assuming losses come from triple duplicate acknowledgements, what shape does TCP's congestion control have?

What to remember

  • The sender’s rate is roughly cwnd/RTT, so adjusting the window is adjusting the rate. A loss event is a timeout or three duplicate acknowledgements, and nothing else counts.
  • Slow start begins at 1 MSS and doubles every round trip; the name is about where it starts. Congestion avoidance then adds one MSS per round trip.
  • A timeout always drops cwnd to 1 MSS and returns to slow start. Three duplicates halve it, add 3 MSS, and enter fast recovery — because duplicates prove the network is still delivering.