Worked examples — TCP reliability — seq - ack numbers, retransmission, cumulative ACK
This page is a drill. We take the machinery from the parent topic — sequence numbers, cumulative ACKs, timeouts, fast retransmit — and hit every case class you could be asked about: clean delivery, single loss, multiple loss, reordering, duplicate segments, ACK loss, wraparound, and the RTO arithmetic.
Every symbol used here is defined first. If you have not seen it, read the callout, then the figure, then the steps.
The scenario matrix
Every TCP-reliability question is one of these cells. Click any example link to jump to its worked steps. The 🖼 marks the cells whose dynamics are drawn as a figure.
| # | Case class | What is being tested | Jump |
|---|---|---|---|
| C1 | Clean, in-order delivery 🖼 | plain SEQ→ACK arithmetic | Ex 1 |
| C2 | Single loss, later segments arrive 🖼 | duplicate ACKs, buffering | Ex 2 |
| C3 | Fast retransmit trigger (3 dup ACKs) 🖼 | the "3 dup" rule + ACK leap | Ex 3 |
| C4 | Timeout retransmit (RTO expiry) | when no dup ACKs come | Ex 4 |
| C5 | RTO arithmetic (Jacobson/Karels) | SRTT, RTTVAR, RTO numbers | Ex 5 |
| C6 | Reordering (no loss) — degenerate "false alarm" | why dup ACK ≠ loss | Ex 6 |
| C7 | Lost ACK self-heals | robustness of cumulative ACK | Ex 7 |
| C8 | Duplicate segment received twice | idempotent ACKs, discard-and-reACK | Ex 8 |
| C9 | SYN/FIN phantom byte (+1 edge case) | handshake sequence math | Ex 9 |
| C10 | Sequence-number wraparound (limiting value, 32-bit) | modular arithmetic edge | Ex 10 |
| C11 | Word problem / exam twist | multi-loss, pick the mechanism | Ex 11 |
Example 1 — Clean in-order delivery (C1)
This is the "clean staircase" panel of the figure above — the water level rises one full step per arrival, never hitting a wall.
Steps
- Segment 1: SEQ , covers . Why this step? By definition SEQ is the first byte; adding the length gives the half-open interval.
- Each following segment starts where the last ended: . Why? "Next segment in order starts at SEQ+L." No gaps, so we just keep adding 200.
- ACK after each = last contiguous byte = end-of-interval:
- after Seg1:
- after Seg2:
- after Seg3:
- after Seg4: Why? The end of a half-open interval is exactly "next expected byte", so ACK equals the upper bound .
Verify: Segment 4 covers ; highest contiguous byte ; . ✓ Matches. Total bytes , and . ✓
Example 2 — Single loss, later segments buffered (C2)
This is the middle panel of the staircase figure. Read that panel like this: the water level climbs to 1100 when A lands, then hits the orange wall at the missing B. C and D arrive past the wall, so they sit in the buffer (the faded violet bricks) but the level cannot rise — it stays pinned at 1100. That flat stretch is exactly the repeated ACK 1100 you compute below. What to notice in the figure: the level line is horizontal for three arrivals in a row — a horizontal ACK line always means "stuck behind a hole."
Steps
- A arrives → ACK . Why? Contiguous up to 1099, next wanted = 1100.
- C arrives but 1100–1199 is missing → gap → receiver re-sends ACK . Why? Highest contiguous (no-gap) byte is still 1099. C is buffered but cannot count.
- D arrives → still a gap → ACK again. Why? Same reason: the hole at B has not been filled.
Verify: ACK sequence . Only one distinct value appears (1100), repeated 3 times — a "dup ACK" storm. The end of A's interval is , confirming the stuck value. ✓
Example 3 — Fast retransmit fires (C3)
This is the right panel of the staircase figure: after the flat stretch, the level leaps vertically in one move once B is delivered — that single tall jump is what you are computing.
Steps
- Sender counts duplicate ACKs for 1100: dup #1, dup #2, dup #3. Why? Three dups (four identical ACKs total) is the agreed threshold that means "loss, not mere reorder."
- On the 3rd dup, sender fast-retransmits the segment at SEQ (i.e. B) — without waiting for RTO. Why? The stuck ACK value 1100 literally names the byte the receiver is missing.
- B arrives → receiver now holds B,C,D contiguously → ACK leaps to 1400. Why? Cumulative ACK reports the whole contiguous prefix at once; filling the single hole connects all buffered data.
Verify: After B fills , the contiguous run is , highest byte , so ACK . ✓ It does not climb in steps.
Example 4 — Timeout retransmit, no dup ACKs (C4)
Steps
- No further data → receiver never sees an out-of-order arrival → zero duplicate ACKs. Why? Fast retransmit needs 3 dup ACKs, which need later segments to arrive. There are none.
- So only the timer can rescue this. Sender started the RTO clock at . Why? Timeout is the fallback when the ACK-based hint is unavailable.
- At ms with no ACK covering byte 8000, RTO expires → retransmit segment at SEQ , and double RTO to ms (exponential backoff). Why double? If the network is congested, backing off avoids hammering it.
Verify: Retransmit time ms (one RTO). Mechanism timeout, not fast retransmit (impossible here). New RTO after backoff ms. ✓
Example 5 — RTO arithmetic, Jacobson/Karels (C5)
Recall the smoothing formulas (each new value is mostly the old estimate plus a small pull toward the fresh sample):
Steps
- RTTVAR first, using the old SRTT (this is the standard order): ms. Why old SRTT? The variance measures how far the sample strayed from the previous estimate.
- SRTT next: ms. Why? weight nudges the average gently toward the fresh 140.
- RTO: ms. Why RTTVAR? Jittery links get a wider safety margin so we don't retransmit good data.
Verify: SRTT , RTTVAR , RTO ms — closer to 200 than 300. ✓ (Units all ms, consistent.)
Example 6 — Reordering, no loss (degenerate false alarm) (C6)
Steps
- A arrives → ACK . Why? Contiguous to 1099.
- C arrives (early, before B) → gap at 1100–1199 → dup ACK (dup #1). Why? The receiver has C but the no-gap rule still points at 1100. A dup ACK means "I got something past the hole," not "data was lost."
- B arrives shortly after → hole filled → contiguous now → ACK 1300 in one jump. Why? Only one dup ACK occurred (need 3 to fast-retransmit), so the sender never resends — reordering healed itself.
Verify: ACK sequence . Distinct dup count → no fast retransmit. Final ACK end of . ✓ This is the classic proof that "dup ACK ≠ loss."
Example 7 — A lost ACK self-heals (C7)
Steps
- Sender gets ACK 2100 → knows A is safe. Why? Cumulative ACK 2100 confirms all bytes .
- ACK 2200 is lost → sender does not learn about B yet. Why note this? A naive scheme would time out and resend B.
- ACK 2300 arrives → it cumulatively confirms everything below 2300, i.e. A and B and C. Why? Because cumulative ACK reports the whole prefix, the later ACK subsumes the lost one. No retransmission needed.
Verify: Final ACK 2300 covers bytes , which includes B's range . The lost ACK 2200 became irrelevant. ✓ (This is the "robust" half of cumulative ACK.)
Example 8 — Duplicate segment arrives twice (C8)
Steps
- A arrives → ACK ; B arrives → ACK . Receiver's water level is now . Why? Standard clean delivery: contiguous prefix .
- Duplicate A (SEQ 4000, bytes ) arrives. The receiver checks: is any byte in still needed? Every byte is already delivered, so all of A's bytes are old. Why check? The receiver only cares about bytes at or above its current level. Bytes wholly below it are already delivered to the app.
- Receiver discards the duplicate (it does not re-deliver those bytes to the app — that would corrupt the stream with repeats) and re-sends the current ACK . Why re-ACK, and why not go backward? The ACK is always "next byte wanted" = current level = . It cannot decrease. Re-sending tells the sender "your retransmit was unnecessary; I'm already past this." ACKs are idempotent: repeating one never harms correctness.
Verify: Level before dup ; a duplicate of already-delivered bytes cannot lower it, so ACK stays (never 4100). ✓ Bytes delivered to the app across the whole exchange bytes, not 300 — the duplicate added zero. ✓
Example 9 — SYN/FIN phantom byte (+1 edge case) (C9)
Steps
- SYN carries no data, but the SYN flag consumes one sequence number (a "phantom byte"). Why? So the handshake itself can be reliably acknowledged and retransmitted like real data.
- Server ACK . Why? Cumulative-ACK rule applied to the phantom byte at position : next expected .
- The client's first real data byte therefore starts at SEQ . Why? Position 3000 was "used up" by the SYN; data continues from 3001.
Verify: ACK ; first data SEQ . ✓ (FIN behaves identically: it too consumes one sequence number, so the same appears at connection close.)
Example 10 — Sequence-number wraparound (limiting value) (C10)
Steps
- This segment covers positions starting at for 100 bytes; the naive end . Why? Same interval arithmetic as always — until we exceed the counter's range.
- That value exceeds , so we take it modulo : . Why modulo? The 32-bit field silently wraps: after the maximum value it rolls back to 0.
- So the next segment's SEQ . Why does this still work? TCP compares sequence numbers using modular ("wrapped") distance, so 70 correctly reads as "just after ", not as "way back at the start."
Verify: . ✓ The 30 bytes before the wrap () plus 70 bytes after . ✓
Example 11 — Exam twist: multiple losses, pick the mechanism (C11)
First, the SEQ map (each segment bytes):
| Seg | SEQ | covers |
|---|---|---|
| S1 | 10000 | [10000,10160) |
| S2 | 10160 | [10160,10320) lost |
| S3 | 10320 | [10320,10480) |
| S4 | 10480 | [10480,10640) lost |
| S5 | 10640 | [10640,10800) |
| S6 | 10800 | [10800,10960) |
Steps
- S1 arrives → ACK . Why? Contiguous to 10159.
- S3 arrives → gap (S2 missing) → dup ACK . S5 arrives → dup . S6 arrives → dup . Why? The first hole is at 10160; every later arrival keeps the ACK stuck there.
- After 3 dup ACKs for 10160 → fast-retransmit S2 at SEQ 10160. Why? The stuck ACK value 10160 names the first missing byte only — it says nothing about S4.
- (b) S2 arrives → contiguous now runs (S1,S2,S3) but stops at the next hole (S4). → ACK 10480. Why does it stall at 10480 instead of reaching 10960? Cumulative ACK advances only up to the first remaining gap. S5 and S6 are buffered but S4's hole is a wall between the level and them, so the water rises only to S4's start .
- (c) With plain cumulative ACK the receiver could never announce "I already hold S5 and S6" — it can only report the first hole (10480). So the sender is blind to what's buffered past the second hole and must run a second dup-ACK round to rescue S4, wasting a round-trip. SACK was added precisely so the receiver can list the non-contiguous ranges it already has (e.g. "I have [10640,10960) too"), letting the sender resend only S4 immediately. Why this matters: every extra hole under cumulative ACK costs another stall; SACK collapses all holes into one report.
Verify: (a) ACKs (one real + three dups). (b) After S2 fills, contiguous prefix , so ACK (stalls at S4's hole, not 10960). ✓ End-of-S6 would have been , unreachable until S4 is also repaired. ✓
Recall Self-test: which cell is each?
A receiver keeps sending the same ACK three times while buffering later data. ::: Single loss → heading toward fast retransmit (C2/C3). One lone segment is lost with no traffic behind it. ::: Only a timeout can repair it (C4). A dup ACK appears but no retransmit happens. ::: Reordering, a false alarm (C6). The same data segment arrives twice; the ACK does not move. ::: Duplicate segment, idempotent re-ACK (C8). SEQ plus 100 bytes gives SEQ 70. ::: Wraparound modulo (C10). A SYN with ISN is acknowledged with . ::: Phantom-byte +1 (C9).