Course outline · 0% complete

0/30 lessons0%

Course overview →

Windows, flow control, and congestion control

lesson 7-3 · ~14 min · 21/30

Waiting for every ack would be unusable

Imagine TCP sent one segment, waited for its ack, then sent the next. That is stop and wait, and its throughput is one segment per round trip.

segment 1460 bytes, RTT 60 ms
throughput = 1460 ÷ 0.060 = 24,333 bytes per second
           = about 0.19 Mbps

On a gigabit link. The link is not the limit, the waiting is. The fix is pipelining: keep several segments in flight at once, and only wait when a limit is reached. That limit is the window.

in flight allowed = min(receive window, congestion window)

Two separate limits with two separate purposes, and confusing them is the most common misunderstanding in this unit.

Flow controlCongestion control
Protectsthe receiver from being overrunthe network from being overloaded
Limit is calledreceive window, rwndcongestion window, cwnd
Who sets itthe receiver, in the window fieldthe sender, by inference
Signalan explicit number in every segmentloss, delay, or an ECN mark
Failure without itdropped data, wasted retransmitsqueue collapse for everyone

The sender may send only as much as the smaller of the two permits. Flow control is easy because the receiver simply says the number. Congestion control is hard because nobody tells the sender anything, so it has to guess from evidence.

Flow control, the receiver's brake

Every segment a receiver sends carries a window value, meaning "I have this many bytes of free buffer right now". The sender must keep unacknowledged data at or below it.

application is reading slowly, so the buffer fills

ack 5001 win 64000   plenty of room
ack 9001 win 32000   filling up
ack 13001 win 8000   nearly full
ack 17001 win 0      stop sending

A zero window is not an error, it is a legitimate "pause". The sender stops and sends small window probes periodically until the receiver advertises room again. Seeing zero window segments in a capture is a strong signal that the application on the receiving side is too slow, not that the network is bad.

Because the window field is only 16 bits it maxes at 65,535 bytes, far below the bandwidth delay product of a modern long path. The window scale option from Lesson 7-2 fixes this by agreeing on a shift, so a scale of 7 multiplies the advertised value by 128 and permits windows up to about 8 MB. It is negotiated only in the handshake, so if the SYN or SYN-ACK is stripped by some middlebox, the connection is stuck with 64 KB windows and mysteriously slow transfers for its whole life.

Congestion control: the sender's guess

The network gives one clear signal that it is overloaded, and that signal is loss. Routers with full queues drop packets, so a lost segment means the path is saturated. TCP therefore treats loss as a command to slow down, and its behavior is a cycle of probing upward and backing off.

Slow start. Begin with a small cwnd, typically 10 segments, and double it every round trip. Despite the name it is exponential and fast, and it is how a new connection finds the available capacity within a few round trips.

Congestion avoidance. Once cwnd passes a threshold called ssthresh, or a loss occurs, stop doubling and grow by roughly one segment per round trip instead. Careful probing rather than aggressive searching.

Fast retransmit. Recall the duplicate acks from Lesson 7-2. Three duplicate acks are treated as proof that one segment was lost while later ones arrived, so the sender retransmits that segment immediately rather than waiting for a timeout.

Fast recovery. After a fast retransmit, halve cwnd and continue from there instead of collapsing to the initial value. Only a full timeout, meaning no acks at all, causes the drastic reset to a tiny cwnd, because silence suggests something much worse than one dropped packet.

The result is a sawtooth: climb, halve, climb, halve.

AlgorithmGrowth ruleNotes
Renoclassic climb, halve on lossthe textbook baseline
CUBICcubic function of time since lossLinux default, better on fast links
BBRmodels bandwidth and RTT, ignores loss as a signalavoids filling queues

BBR is worth understanding conceptually, because it corrects a real flaw. Loss based control fills the router's queue until it overflows, which adds latency for everybody sharing that queue. That effect is called bufferbloat, and it is why a big upload makes an unrelated video call stutter even though bandwidth remains. BBR instead estimates the actual bottleneck bandwidth and minimum RTT and paces itself to keep the queue nearly empty.

cwndtimeslow startcongestion avoidanceloss, halvetimeout, reset
The congestion window climbs quickly during slow start, then grows slowly, halving each time a loss is detected. A timeout resets it much further.

Retransmission timers and real numbers

Fast retransmit needs later segments to arrive. When nothing arrives at all, there is only the clock, and that clock is the RTO (retransmission timeout).

- TCP measures each round trip and keeps a smoothed average plus a deviation. RTO is roughly the average plus four deviations, with a floor of around 200 ms on Linux. - Every time a retransmission of the same segment also times out, the RTO doubles. That is exponential backoff, the same idea as Lesson 3-1's Ethernet backoff. - The timestamps option lets a sender measure RTT accurately even for retransmitted segments, which fixes a genuine ambiguity: without it, an ack after a retransmission cannot be attributed to the original or the retry, so measuring the RTT from it would be wrong. That fix is known as Karn's algorithm.

Put the numbers together to see why loss hurts so much more than people expect. On a 60 ms path with a 1460 byte MSS, a healthy connection with an 8 MB window is capable of over 1 Gbps. Introduce 1 percent random loss and a loss based algorithm halves its window roughly every 100 segments, holding average throughput to a small fraction of that. This is why "the link has plenty of bandwidth" and "the transfer is slow" happily coexist, and why the first thing to check on a slow transfer is the loss counter and the retransmission count.

ss -ti dst 93.184.216.34
ESTAB 0 0 192.168.1.42:51514 93.184.216.34:443
  cubic wscale:7,9 rto:264 rtt:61.4/2.1 mss:1400
  cwnd:42 ssthresh:31 bytes_sent:2841204 retrans:0/14
  rcv_space:64128 delivery_rate:38.4Mbps

Read it as the whole lesson in one block. The algorithm is cubic, RTT is 61.4 ms with 2.1 ms of variance, cwnd is 42 segments, ssthresh sits at 31 which proves a loss happened earlier, retrans:0/14 counts 14 retransmissions over the connection's life with none outstanding right now, and delivery_rate is the throughput actually achieved.