skip to content

How does TCP's sliding window work: what moves its left and right edges, and how much new data may the sender transmit?

level: middleimportance: must knowfreq 50%

answer

  1. three sender variables
  2. acknowledgments versus application reads
  3. usable window formula
  4. right edge = ACK plus window

basics

~20 s

TCP's sliding window is the byte range a sender may have in flight. Acknowledgments move the left edge (SND.UNA); the receiver's advertised window, which grows as its application reads, sets the right edge. Usable = SND.UNA + SND.WND - SND.NXT.

solid answer

~40 s

The sender tracks three variables from RFC 9293: `SND.UNA`, the oldest unacknowledged byte; `SND.NXT`, the next byte to send; and `SND.WND`, the window the receiver last advertised. The window runs from `SND.UNA` to `SND.UNA + SND.WND`. An acknowledgment moves the **left edge**; the **right edge** is the acknowledgment number plus the advertised window, so it only advances when the receiver frees buffer space by its application reading. The sender may send `SND.UNA + SND.WND - SND.NXT` new bytes, the usable window. An ACK does not automatically open room for what it acknowledges: if the reader is idle, the window shrinks by the same amount and the right edge stays put.

code

pseudocode · 10 lines
pseudocode
on ACK segment (SEG.SEQ, SEG.ACK, SEG.WND):
  if SND.UNA < SEG.ACK <= SND.NXT:
      SND.UNA = SEG.ACK              # left edge moves right
  if SND.UNA <= SEG.ACK <= SND.NXT:
      if SND.WL1 < SEG.SEQ or (SND.WL1 == SEG.SEQ and SND.WL2 <= SEG.ACK):
          SND.WND = SEG.WND          # right edge = SND.UNA + SND.WND
          SND.WL1 = SEG.SEQ
          SND.WL2 = SEG.ACK

usable = SND.UNA + SND.WND - SND.NXT # send new data only if usable > 0

go deeper

for a junior

Recall that the window is a range of byte sequence numbers the sender may have unacknowledged, and that it slides forward as data is acknowledged.

for a middle

Explain the three variables, which event moves each edge, and compute the usable window from SND.UNA, SND.NXT and SND.WND on a worked example.

for a senior

Show you can read a trace where ACKs arrive but the right edge does not move and conclude the receiving application is not reading.

for a principal

Discuss why the design ties the right edge to application reads: it pushes backpressure from a slow consumer all the way to the producer without any extra signalling.

## The window as a range of sequence numbers TCP numbers every **byte** of the stream. At any moment a sender's view of the sequence space splits into four regions, which RFC 9293 draws as the **send sequence space** using three variables: - `SND.UNA` (send unacknowledged): the oldest byte sent but not yet acknowledged; - `SND.NXT` (send next): the next byte the sender will transmit; - `SND.WND` (send window): the window the peer last advertised, measured as an offset from `SND.UNA`. | Region | Range | Meaning | |---|---|---| | 1 | below `SND.UNA` | sent and acknowledged; can be forgotten | | 2 | `SND.UNA` to `SND.NXT` | sent, awaiting acknowledgment ("in flight") | | 3 | `SND.NXT` to `SND.UNA + SND.WND` | allowed but not yet sent | | 4 | beyond `SND.UNA + SND.WND` | not yet allowed | Regions 2 and 3 together are the window. It **slides** because its edges move as acknowledgments and window advertisements arrive. ## What moves each edge - The **left edge** (`SND.UNA`) moves right when an acknowledgment covers new data. Per RFC 9293, if `SND.UNA < SEG.ACK =< SND.NXT`, the sender sets `SND.UNA` to `SEG.ACK` and drops fully acknowledged segments from its retransmission queue. - The **right edge** (`SND.UNA + SND.WND`) is wherever the receiver says: the acknowledgment number plus the advertised `Window`. It moves right only when the receiver has freed buffer space, which happens when its **application reads**. - The receiver keeps the mirror image: `RCV.NXT` is the next byte it expects, and `RCV.NXT + RCV.WND` is its right edge. The consequence surprises people: an acknowledgment does **not** automatically open room for as many bytes as it acknowledges. If the receiving application has read nothing, the acknowledged bytes still occupy the receive buffer, the advertised window drops by the same amount, and the right edge stays where it was. ## How much may the sender send? RFC 9293 §3.8.6.2.1 defines the **usable window**: `U = SND.UNA + SND.WND - SND.NXT` That is the offered window minus the data already in flight. A worked trace, in bytes: 1. `SND.UNA` = 1000, `SND.NXT` = 5000, `SND.WND` = 6000. Right edge = 7000; U = 2000. The sender may send 2,000 more bytes. 2. An ACK arrives with acknowledgment 3000 and window 4000, and the receiving application has read nothing. Now `SND.UNA` = 3000, `SND.WND` = 4000; the right edge is still 7000 and U is still 2000. 3. The application reads, and the receiver sends a **window update**: acknowledgment 3000, window 8000. The right edge jumps to 11000 and U = 6000. In practice the sender is also bounded by its congestion window: RFC 5681 lets it have no more than the minimum of `cwnd` and `rwnd` outstanding. The sliding window described here is the receiver's half of that minimum. ## Guarding against stale window reports In a one-way transfer, many acknowledgments carry the same sequence number, so if they arrive reordered an old, smaller window could overwrite a newer one. RFC 9293 records the segment that last updated the window in `SND.WL1` (its sequence number) and `SND.WL2` (its acknowledgment number), and accepts a new window only when `SND.WL1 < SEG.SEQ`, or when `SND.WL1 = SEG.SEQ` and `SND.WL2 =< SEG.ACK`. Old segments therefore cannot drag the window backwards. ## Edge cases worth knowing - **Negative usable window.** RFC 9293 says a receiver SHOULD NOT *shrink* the window (move the right edge left), but a sender MUST be robust if it happens. U can then go negative, and the sender SHOULD NOT send new data while it does. - **Zero window.** When the advertised window reaches zero, U cannot be positive and the sender must probe; that is its own mechanism. - **Windows are bytes.** All of the arithmetic above is in octets, and sequence comparisons are made modulo 2^32. - **Window updates carry no data.** When the receiving application reads, the receiver announces the new right edge in an acknowledgment that may carry no payload at all. Such a segment repeats the last acknowledgment number and changes only the `Window` field, which is why the `SND.WL1`/`SND.WL2` check accepts a segment whose sequence and acknowledgment numbers merely equal the last ones. - **Both directions are independent.** Each endpoint is a sender and a receiver at once, so a connection has two sliding windows, one per direction, each advertised by the side that receives that direction's data.

  • Why does a TCP sender check SND.WL1 and SND.WL2 before accepting a new window?
    In a one-way transfer many acknowledgments carry the same sequence number, so reordered segments could let an old, smaller window overwrite a newer one. RFC 9293 records the sequence and acknowledgment numbers of the segment that last updated the window and accepts a new window only from a segment at least that recent, so stale reports cannot drag the window backwards.
  • Why is the TCP window expressed relative to the acknowledgment number rather than as an absolute sequence number?
    The receiver's buffer starts at the next byte it expects, so an offset from the acknowledgment number describes exactly the range it can accept. An offset also keeps the field small: 16 bits suffice where an absolute edge would need 32. The right edge is simply acknowledgment plus window, which both sides can compute.

saying these in an interview costs you the question

  • Every ACK opens room for exactly as many new bytes as it acknowledges.
  • The left edge of the window moves when the receiving application reads.
  • A sender may send a full window of new data each time an ACK arrives.
  • The window is counted in segments, so it moves one packet at a time.
  • A receiver that has read nothing still advertises its full buffer size.