Introduction

Flow control stops a sender from overrunning its receiver. It does nothing for the routers in between: if many senders together offer more traffic than a link can carry, router queues fill, packets are dropped, senders retransmit, and the network can spend its capacity on retransmissions (congestion collapse). TCP therefore keeps a second window, the congestion window cwnd, and permits at most max⁡(0,min⁡(cwnd,rwnd)−FlightSize)\max(0,\min(\text{cwnd},\text{rwnd})-\text{FlightSize}) new bytes. Already-sent data may temporarily exceed a reduced window.

In the classic loss-based Tahoe/Reno model used here, without ECN, TCP infers congestion from loss rather than explicit network congestion notifications. The loss indications considered here are a retransmission timeout or three duplicate ACKs. It probes for capacity by growing cwnd and backs off sharply on loss: exponential growth in slow start, linear growth in congestion avoidance (additive increase), and a cut on loss (multiplicative decrease). TCP Tahoe and TCP Reno differ in what they do after three duplicate ACKs.

Prerequisites

  • TCP Flow Control: the receive window, the sender's window, and the bound min⁡(rwnd,cwnd)\min(\text{rwnd}, \text{cwnd}).

Core concepts

State variables

VariableMeaning
cwndCongestion window: how much the sender lets itself have in flight
ssthreshSlow-start threshold: below it, slow start; at or above it, congestion avoidance
MSSMaximum segment size; cwnd and ssthresh are counted in MSS units (RFC 5681 counts bytes)

Slow start

Start with a small window (the examples here start at 1 MSS; RFC 5681 allows an initial window of 2 to 4 segments depending on MSS). For each ACK of new data, cwnd grows by 1 MSS. Since a window of ww segments returns ww ACKs in one RTT, cwnd doubles every RTT: 1, 2, 4, 8, ... The name is historical; the growth is exponential.

Congestion avoidance

Once cwnd ≥\ge ssthresh, cwnd grows by about 1 MSS per RTT (per ACK by MSS×MSS/cwnd\text{MSS} \times \text{MSS} / \text{cwnd}): linear growth.

Reaction to loss

EventssthreshcwndThen
Timeout (Tahoe and Reno)max⁡(FlightSize/2,2)\max(\text{FlightSize}/2, 2)1Slow start
Three duplicate ACKs, Tahoemax⁡(FlightSize/2,2)\max(\text{FlightSize}/2, 2)1Fast retransmit, then slow start
Three duplicate ACKs, Renomax⁡(FlightSize/2,2)\max(\text{FlightSize}/2, 2)ssthresh (after fast recovery)Fast retransmit, fast recovery, then congestion avoidance

FlightSize is the amount of outstanding data, which in the per-RTT model equals the current cwnd.

Fast retransmit. Three duplicate ACKs (four ACKs with the same number) mean later segments are arriving, so one segment is probably lost but the path still works: retransmit it at once, without waiting for the timer.

Fast recovery (Reno). After fast retransmit, set cwnd = ssthresh + 3 MSS (the three segments that left the network), add 1 MSS for each further duplicate ACK, and when the ACK for new data arrives, set cwnd = ssthresh and continue in congestion avoidance. A timeout is treated as more serious than three duplicate ACKs, because nothing is getting through.

Conventions for round-by-round problems

The tables use the following conventions; a question that states others overrides them.

  1. Time is counted in transmission rounds (RTTs); cwnd is in MSS and constant within a round.
  2. In slow start the next cwnd is min⁡(2 cwnd,ssthresh)\min(2\,\text{cwnd}, \text{ssthresh}): growth stops at ssthresh rather than overshooting it.
  3. When cwnd ≥\ge ssthresh, the next cwnd is cwnd + 1.
  4. A loss event in a round sets ssthresh = ⌊cwnd/2⌋\lfloor \text{cwnd}/2 \rfloor (at least 2), using that round's cwnd, and determines the next round's cwnd: 1 after a timeout or Tahoe triple duplicate ACK, ssthresh after a Reno triple duplicate ACK. Fast recovery's temporary inflation is not shown.

How it works: Tahoe and Reno, round by round

Initial cwnd = 1, ssthresh = 16. Three duplicate ACKs occur in round 9 and a timeout in round 13.

RoundTahoe cwndTahoe ssthreshReno cwndReno ssthreshEvent
1116116
2216216
3416416
4816816
516161616reaches ssthresh
617161716
718161816
819161916
9201620163 duplicate ACKs
101101010
112101110
124101210
138101310timeout
141416
152426
164446
175466
186476
197486
208496

After round 9 both set ssthresh = 10; Tahoe restarts at 1, Reno continues from 10. At the timeout in round 13 the two have different windows (8 and 13), so they get different thresholds, 4 and ⌊13/2⌋=6\lfloor 13/2 \rfloor = 6, and then both restart at 1.

A plot of cwnd in MSS (vertical axis, 0 to 22) against transmission round (horizontal axis, 1 to 20) with two lines. Both rise 1, 2, 4, 8, 16 in rounds 1 to 5, then 17, 18, 19, 20 in rounds 6 to 9, where a marker reads "3 duplicate ACKs". The Tahoe line drops to 1 in round 10 and climbs 2, 4, 8; the Reno line drops to 10 and climbs 11, 12, 13. A marker at round 13 reads "timeout": Tahoe falls from 8 and Reno from 13, both to 1 in round 14. Tahoe then climbs 2, 4, then linearly 5, 6, 7, 8 (ssthresh 4); Reno climbs 2, 4, 6, then 7, 8, 9 (ssthresh 6). Dotted horizontal lines mark ssthresh at 16, then 10, then 4 for Tahoe and 6 for Reno.
Figure 1. cwnd per round for Tahoe and Reno with ssthresh 16, three duplicate ACKs in round 9 (cwnd 20) and a timeout in round 13: Tahoe restarts from 1 both times, Reno resumes at 10 after the duplicate ACKs.

Mathematical explanation

Slow-start time. From cwnd = 1, reaching ww (a power of 2 not above ssthresh) takes lg⁡w\lg w rounds, and sending nn segments in slow start takes ⌈lg⁡(n+1)⌉\lceil \lg (n + 1) \rceil rounds, since after kk rounds 2k−12^k - 1 segments have gone.

Recovering after a timeout. Assume the pre-loss flight fills cwnd WW, in MSS units. Set ssthresh=max⁡(W/2,2)\text{ssthresh}=\max(W/2,2) and cwnd = 1. For powers of two W≥4W\ge4, slow start takes lg⁡(W/2)\lg(W/2) growth rounds to reach the threshold, followed by W/2W/2 congestion-avoidance rounds to reach WW. For W=32W=32: 4 + 16 = 20 rounds. For W=1W=1 the old window is already reached (0 growth rounds); for W=2W=2, one slow-start growth round suffices. Otherwise use the stated round-by-round recurrence and minimum threshold, not this restricted closed form.

AIMD sawtooth. If a Reno flow loses one packet each time its window reaches WW, the window cycles linearly between W/2W/2 and WW, so its average is 34W\tfrac34 W and its throughput about 0.75 W⋅MSS/RTT0.75\,W \cdot \text{MSS} / \text{RTT}.

Why AIMD. Additive increase and multiplicative decrease move competing flows towards an equal share: each decrease halves the difference between two flows' windows, while each increase leaves it unchanged.

Worked examples

Example 1: time to send a file in slow start

A file of 63 segments, cwnd starting at 1, ssthresh 64, no loss: rounds carry 1, 2, 4, 8, 16, 32 segments; 1+2+4+8+16+32=631 + 2 + 4 + 8 + 16 + 32 = 63, so 6 RTTs. A 64-segment file needs a seventh round for its last segment.

Example 2: back to the old window

MSS = 1 KB. A timeout occurs at cwnd = 32 KB. ssthresh = 16 KB, cwnd = 1 KB; slow start gives 1, 2, 4, 8, 16 (4 RTTs to reach 16), then 17, ..., 32 (16 RTTs). cwnd is 32 KB again 20 RTTs after the timeout.

Example 3: halving an odd window

Reno with cwnd = 13 MSS gets three duplicate ACKs. ssthresh = ⌊13/2⌋=6\lfloor 13/2 \rfloor = 6 MSS and, after fast recovery, cwnd = 6. Under RFC 5681 in bytes, with MSS 1460 and FlightSize 13×1460=18,98013 \times 1460 = 18{,}980 bytes, ssthresh = 9490 bytes = 6.5 MSS; problems in MSS units round down unless they say otherwise.

Example 4: fast-recovery window in detail

Reno, cwnd = 20 MSS, three duplicate ACKs. ssthresh = 10; cwnd = 10 + 3 = 13 while the retransmission is outstanding; two more duplicate ACKs inflate it to 15; the ACK for new data sets cwnd = 10. The per-round tables show only the final 10.

Example 5: a threshold that is not a power of 2, and a receiver window

cwnd starts at 1 MSS, ssthresh = 12 MSS, rwnd = 14 MSS, no loss; each round the sender sends min⁡(cwnd,rwnd)\min(\text{cwnd}, \text{rwnd}) segments.

Round1234567891011
Segments sent124812131414141414

Slow start stops at 12 (convention 2), congestion avoidance adds 1, and from round 7 the receiver's window of 14 is the limit. After 7 rounds 54 segments have gone, so a 100-segment file needs 11 rounds (54+4×14=110≥10054 + 4 \times 14 = 110 \ge 100). If a problem lets slow start overshoot (8 doubles to 16, then +1 per round), the rounds send 1, 2, 4, 8, 14, 14, and so on: 29 segments after 5 rounds and again 11 rounds for 100 segments, but the per-round values differ, so the convention must be stated.

Reno leaves fast recovery on the first new ACK; if several segments of one window were lost, that ACK is only a partial ACK and Reno may need a timeout. NewReno (RFC 6582) stays in fast recovery until all data outstanding at the time of the loss is acknowledged, retransmitting one lost segment per partial ACK.

Important properties and observations

  • Loss is the signal. Classic TCP reads every loss as congestion, which hurts on lossy wireless links.
  • Self-clocking. ACKs pace new transmissions at the rate the bottleneck delivers.
  • The sender uses min⁡(cwnd,rwnd)\min(\text{cwnd}, \text{rwnd}); whichever is smaller limits throughput.
  • Timeout is harsher than duplicate ACKs in Reno: back to 1 against back to ssthresh.

Common misconceptions.

  • "Slow start grows slowly." It doubles every RTT.
  • "Tahoe and Reno differ on timeout." Both go to cwnd = 1 on timeout; they differ on three duplicate ACKs.
  • "ssthresh is halved." ssthresh is set to max(FlightSize/2, 2 MSS), not half of the old ssthresh.
  • "Three ACKs mean three duplicates." Three duplicate ACKs means four ACKs with the same number.
  • "cwnd is advertised by the receiver." rwnd is; cwnd is the sender's own estimate.

GATE examination perspective

  • Round-by-round tables. State the conventions; double up to ssthresh, then +1; on loss, ssthresh = max(FlightSize/2, 2 MSS) (FlightSize = cwnd only for a full flight).
  • Tahoe versus Reno. Same on timeout; on three duplicate ACKs Tahoe goes to 1, Reno to the new ssthresh.
  • Counting RTTs. Starting from cwnd = 1 MSS, reaching a power-of-two window ww not above ssthresh takes lg⁡w\lg w growth rounds. Sending n≥1n\ge1 segments wholly in slow start takes ⌈lg⁡(n+1)⌉\lceil\lg(n+1)\rceil transmission rounds. Congestion avoidance grows by one MSS per growth round.
  • Bytes versus segments. Convert KB windows to MSS units first; watch whether KB means 1000 or 1024.
  • Receiver limit and odd thresholds. Send min⁡(cwnd,rwnd)\min(\text{cwnd}, \text{rwnd}) per round; a threshold that is not a power of 2 cuts the last slow-start step short (Example 5).
  • Throughput. min⁡(cwnd,rwnd)/RTT\min(\text{cwnd}, \text{rwnd}) / \text{RTT} at any moment; 0.75 W/RTT0.75\,W/\text{RTT} on average for the sawtooth.

Summary

Sources

  1. A. S. Tanenbaum, D. J. Wetherall, Computer Networks, 5th ed., Pearson, 2011: Section 6.5.10 (TCP Congestion Control).
  2. J. F. Kurose, K. W. Ross, Computer Networking: A Top-Down Approach, 8th ed., Pearson, 2021: Sections 3.6 (Principles of Congestion Control) and 3.7.1 (Classic TCP Congestion Control).
  3. B. A. Forouzan, Data Communications and Networking, 5th ed., McGraw-Hill, 2013: Chapter 24 (Internet Transport-Layer Protocols).
  4. M. Allman, V. Paxson, E. Blanton, RFC 5681, TCP Congestion Control, 2009, Sections 3.1 (Slow Start and Congestion Avoidance, initial window, equation (4)) and 3.2 (Fast Retransmit/Fast Recovery), (consulted 2026-11-11).
  5. T. Henderson, S. Floyd, A. Gurtov, Y. Nishida, RFC 6582, The NewReno Modification to TCP's Fast Recovery Algorithm, 2012, (consulted 2026-11-11).

Next steps