Principles of Reliable Data Transfer

Imagine sending packets over a channel that flips bits, drops packets outright, and delivers survivors out of order. Somehow, protocols built on top of this chaos — most famously TCP — hand applications a perfect, ordered, byte-for-byte stream. This article explains the machinery that makes that illusion possible. It covers the principles of reliable data transfer, abstracted as a protocol called RDT. We build this protocol up in careful stages. This matters because almost every reliable protocol in networking, storage, and distributed systems shares the same handful of ideas. These ideas are acknowledgments, sequence numbers, timers, and retransmission. Once you understand why a single unacknowledged bit is not enough, the rest follows. You'll also see why sequence numbers must exist. And you'll grasp why sliding-window pipelining is necessary for performance. Then you can read the TCP Connection Management and TCP Flow Control articles differently. They become applied special cases rather than mysterious rulebooks. The big picture is a design exercise in layers of assumptions. We start with a perfect channel (trivial). Then we introduce bit errors (need acknowledgments and checksums). Then we introduce loss (need timers). Then we introduce delay and the desire for throughput (need pipelining). Finally, we allow reordering and duplication (need sequence numbers and windows). Each version of the protocol is a strict refinement of the previous one. Each new mechanism is introduced only because a concrete failure mode demands it. It is never introduced speculatively. This article delivers a complete derivation of reliable data transfer from first principles. It includes the finite-state-machine specification of each RDT version. It includes worked traces showing exactly how corruption and loss are recovered from. It covers the two classic pipelined protocols Go-Back-N (GBN) and Selective Repeat (SR). It also covers their complexity and window-size tradeoffs. Finally, it covers the historical context of how these ideas were formalized. Building on Transport-Layer Services and Multiplexing and UDP: Connectionless Transport, you'll see precisely what UDP leaves out. You'll also see what any protocol must add back to become reliable.

Key takeaways
  • Reliable data transfer (RDT) is built in layers: handle bit errors first, then loss, then reordering/duplication, then performance.
  • ACKs plus checksums detect corruption; timers plus retransmission handle silent loss that ACKs alone cannot reveal.
  • Sequence numbers (as small as 1 bit under stop-and-wait) disambiguate retransmissions from new data and duplicate ACKs from new ACKs.
  • Stop-and-wait wastes bandwidth on long links; pipelining (sending multiple unACKed packets) is required for throughput.
  • Go-Back-N uses a single cumulative ACK and retransmits the whole window on loss; Selective Repeat ACKs and retransmits individually.
  • Window size, sequence-number space, and round-trip time jointly determine achievable throughput and correctness constraints.
The core insight

Reliability over an unreliable channel is manufactured entirely from three primitives — acknowledgments, timers, and sequence numbers — combined so that every failure mode (corruption, loss, duplication, reordering) has a distinguishable signature.

The idea

Reliable data transfer is the guarantee that a stream of data handed to a sender's transport entity arrives at the receiver's transport entity, in order, without loss, duplication, or corruption, despite an unreliable channel beneath it. The channel may corrupt bits, drop packets entirely, delay them arbitrarily, or deliver them out of order — but never fabricates new correct packets from nothing. The protocol entity above the channel — call it the RDT sender and RDT receiver — must add exactly enough bookkeeping to detect and correct every one of these faults, at the transport or (in other systems) link/application layer. The design method used throughout this article is incremental refinement of a finite-state machine. We define a protocol as a state machine with well-defined events (calls from the layer above, calls from the layer below, timer expirations) and actions (sending a packet, delivering data upward, starting or stopping a timer). We begin with an idealized, perfectly reliable channel and progressively remove assumptions — first allowing bit errors, then packet loss — adding just enough new state and just enough new message fields to restore correctness at each step. This method is pedagogically important because it shows that ACKs, NAKs, checksums, sequence numbers, and timers are not arbitrary engineering choices; each is the minimal fix for a specific channel defect. A key terminological distinction recurs throughout: stop-and-wait protocols send one packet, wait for its acknowledgment, then send the next — simple but throughput-limited on high delay-bandwidth-product links. Pipelined protocols allow multiple packets to be outstanding (unacknowledged) simultaneously, bounded by a window. The two canonical pipelined protocols, Go-Back-N and Selective Repeat, differ in how much state the receiver keeps and how surgically the sender retransmits after a loss — a tradeoff between receiver simplicity and network efficiency that recurs in TCP Flow Control.

How it works

We build the protocol in four numbered versions, following the standard derivation.

RDT 1.0 — reliable channel. If the underlying channel never corrupts or loses bits, the sender simply packages data and sends it; the receiver simply extracts and delivers it. No acknowledgments, no sequence numbers, no timers are needed — there is nothing to detect or correct.

RDT 2.0 — channel with bit errors only (no loss). Now packets can arrive corrupted, but every packet that is sent does arrive (possibly damaged). The receiver needs a way to detect corruption — a checksum — and a way to tell the sender whether to move on or retransmit: ACK (acknowledged, correctly received) or NAK (not acknowledged, please resend). The sender waits for an ACK/NAK before sending the next unit of data; on NAK it resends the same packet. This works as long as ACKs/NAKs themselves cannot be corrupted. If they can be corrupted, the sender cannot tell an ACK from a NAK, so a garbled feedback message is ambiguous — the sender might needlessly resend a packet the receiver already accepted, causing a duplicate. This motivates the next refinement.

RDT 2.1 / 2.2 — handling corrupted feedback via sequence numbers. Add a 1-bit sequence number (0 or 1) to each data packet. The receiver, upon receiving a garbled ACK/NAK, simply resends its last ACK/NAK (or the sender, on any garbled feedback, just retransmits the current packet). The receiver uses the sequence number to detect duplicate data packets — if it receives a packet with the sequence number of the packet it just acknowledged, it re-ACKs but does not deliver the data again to the application. RDT 2.2 is a variant that eliminates NAKs entirely: the receiver always sends an ACK, but tags it with the sequence number of the packet being acknowledged; a duplicate ACK for the previous packet functions exactly like a NAK for the current one. This NAK-free design is what real protocols (including TCP) actually use.

RDT 3.0 — channel with bit errors AND loss. Now packets (data or ACK) can vanish entirely. Checksums and sequence numbers detect corruption and duplication, but a lost packet produces no feedback at all — the sender would wait forever. The fix is a countdown timer: after sending a packet, the sender starts a timer; if no ACK for that packet arrives before the timer expires, the sender assumes loss (or corruption swallowed the ACK) and retransmits. Because retransmission can create duplicates if the original packet was actually just delayed rather than lost, the 1-bit sequence number from RDT 2.x is still essential to let the receiver silently discard the duplicate while still re-ACKing it. RDT 3.0 is a complete, correct, stop-and-wait reliable transfer protocol over a channel that can corrupt, lose, or reorder-and-delay packets. Its only flaw is performance: it never has more than one packet in flight, so throughput is bandwidth-independent and delay-bound.

Pipelining: Go-Back-N and Selective Repeat. Stop-and-wait's utilization is ``` U = (L / R) / RTT_effective ``` where `L/R` is transmission time and `RTT_effective` includes the full round trip plus the receiver's processing. On a transcontinental link with large delay this ratio can be a small fraction of a percent — utterly wasteful of available bandwidth. The fix is to allow the sender to have up to N unacknowledged packets in flight ("pipelining"), which requires: 1. A larger sequence number range (more than 1 bit), since multiple distinct packets are now unacknowledged simultaneously. 2. Buffering at sender and/or receiver for packets sent-but-not-ACKed, or received-but-not-yet-in-order. 3. A policy for what to retransmit when a loss is detected — this is exactly where GBN and SR diverge.

Go-Back-N (GBN)

The sender maintains a window of size `N`: sequence numbers from `base` (oldest unACKed) to `base + N - 1` (highest allowed to send). The receiver is stateless beyond expecting the next in-order sequence number — it discards any out-of-order packet (even correct ones!) and re-ACKs the last correctly received in-order packet. ACKs are cumulative: an ACK for sequence number `k` means "all packets up to and including `k` have been received correctly." A single timer is kept for the oldest unACKed packet; on timeout, the sender retransmits every packet in the window, hence "go back N." ```pseudocode // ---- GBN Sender ---- state: base = 1, nextseqnum = 1 window size N, timer for packet `base` on rdt_send(data): if nextseqnum < base + N: make packet[nextseqnum] with data, seqnum, checksum send packet[nextseqnum] if base == nextseqnum: start_timer() nextseqnum = nextseqnum + 1 else: refuse_or_buffer(data) // window full on receive(ackpkt) where not corrupt(ackpkt): base = ackpkt.acknum + 1 // cumulative ACK if base == nextseqnum: stop_timer() else: start_timer() // restart for new oldest unACKed on timeout: start_timer() for n = base to nextseqnum - 1: resend packet[n] // resend entire window // ---- GBN Receiver ---- state: expectedseqnum = 1 on receive(pkt): if not corrupt(pkt) and pkt.seqnum == expectedseqnum: deliver_data_to_app(pkt.data) send ACK(expectedseqnum) expectedseqnum = expectedseqnum + 1 else: // corrupt, or out-of-order (too high or duplicate) send ACK(expectedseqnum - 1) // re-ACK last correctly-received discard(pkt) // no buffering of out-of-order data ```

Selective Repeat (SR)

GBN's weakness: one lost packet forces retransmission of the entire window, even packets that already arrived correctly — wasteful when loss rates are non-trivial. Selective Repeat fixes this by having the receiver buffer out-of-order but individually-correct packets, ACK them individually, and having the sender keep a separate timer per packet, retransmitting only the specific packet that timed out. ```pseudocode // ---- SR Sender ---- state: window [send_base, send_base + N - 1] per-packet timer; per-packet status in {unsent, sent, acked} on rdt_send(data): if nextseqnum in window: send packet[nextseqnum] start_timer(nextseqnum) mark_status(nextseqnum, sent) nextseqnum += 1 else: refuse_or_buffer(data) on receive(ACK(n)) where not corrupt: if n in window: mark_status(n, acked) stop_timer(n) if n == send_base: while status(send_base) == acked: send_base += 1 // slide window past acked run // duplicate/old ACK: ignore on timeout(n): resend packet[n] // only THIS packet start_timer(n) // ---- SR Receiver ---- state: rcv_base, buffer of received-but-undelivered packets on receive(pkt) where not corrupt: if pkt.seqnum in [rcv_base, rcv_base + N - 1]: send ACK(pkt.seqnum) if not already_buffered(pkt.seqnum): store_in_buffer(pkt) if pkt.seqnum == rcv_base: while rcv_base is in buffer: deliver_data_to_app(buffer[rcv_base]) remove_from_buffer(rcv_base) rcv_base += 1 else if pkt.seqnum in [rcv_base - N, rcv_base - 1]: send ACK(pkt.seqnum) // re-ACK old packet (its ACK was lost) else: discard(pkt) // outside both windows: ignore ```

Figure
Sliding windows: Go-Back-N vs. Selective Repeat, sequence space and buffering
Go-Back-N (window N=4, no receiver buffering) seq: 1✓ 2✓ 3✗ 4 5 window: [2..5] pkt 3 lost → timeout: resend 3, 4, 5 (whole window, even though 4,5 arrived at receiver but are discarded) Selective Repeat (window N=4, receiver buffers) seq: 1✓ 2✓ 3✗ 4buf 5buf timeout(3) only: resend packet 3 alone; 4 and 5 stay buffered at receiver, delivered once 3 arrives Sender / Receiver windows (SR), sequence number space size ≥ 2N ... unusable ... SENDER window (N) overlap zone RECEIVER window (N) If sequence space = 2N (not 2N+1) the sender and receiver windows could appear to overlap on stale vs. new data — this is exactly why SR requires seq-space size ≥ 2N, proved in the Complexity & Correctness section.

Complexity & correctness

State and message overhead. Stop-and-wait (RDT 3.0) needs only 1 bit of sequence number, O(1) sender/receiver state, and one timer. GBN needs O(N) sender-side buffering (packets that might need retransmission) but O(1) receiver-side state (just `expectedseqnum`) — this asymmetry is its main appeal. SR needs O(N) buffering at both sender and receiver, plus `N` independent timers — more state, but strictly less wasted retransmission.

Throughput. For a link of rate `R`, packet size `L`, and round-trip time `RTT`, stop-and-wait achieves ``` U_stop-and-wait = (L/R) / (RTT + L/R) ``` which for `RTT >> L/R` collapses toward 0. Pipelining with window `N` achieves, while no loss occurs, ``` U_pipelined = min(1, N·(L/R) / (RTT + L/R)) ``` so utilization reaches its maximum (saturating the link) once `N ≥ (RTT + L/R)/(L/R)` — this quantity is the bandwidth-delay product in packets, and it is exactly the number of packets that can be "in the pipe" at once. Choosing `N` below this threshold under-utilizes the link; choosing it (much) above buys nothing further once the link is saturated, and in GBN it makes each loss costlier (a bigger window means more good packets get needlessly discarded and retransmitted).

Why the sequence-number space must be sized correctly — a genuine correctness argument, not a rule of thumb. Go-Back-N needs a sequence-number space of size N + 1 (i.e., `⌈log2(N+1)⌉` bits). Here's why `N` alone is not enough. Suppose the space had exactly `N` values, `0..N-1`. Consider the receiver having correctly received and delivered packets up to sequence number `k`, having sent cumulative ACK `k`, and that ACK is lost. The sender times out and resends packets `k+1 .. k+N` (mod `N`), which are actually sequence numbers `k+1, ..., k+N-1, k` (wrapping). The receiver, expecting `k+1`, will see the resent copy of old packet `k` (a duplicate) land at exactly the same position where a genuinely new packet with sequence number `k` (mod N) could legitimately arrive later in the stream — the receiver cannot distinguish "old duplicate" from "new packet that wrapped around," because both have identical sequence number `k`. With N+1 distinct values, the receiver's one-element memory (`expectedseqnum - 1`) can never collide with any sequence number currently reachable within one window's worth of resend, because the window's span (`N` values) plus the one remembered value fit without collision in `N+1` slots. Selective Repeat needs the sequence-number space to be at least 2N (in practice `2N` is the tight bound; some formulations require `> 2N-1`, i.e., exactly `2N`). Here is the informal argument sketch, made precise: suppose the space were only `N` (equal to the window size). The receiver window `[rcv_base, rcv_base+N-1]` and the sender window `[send_base, send_base+N-1]` could then, after wraparound, occupy the same range of sequence numbers even though one is "old" data whose ACKs were lost and the other is genuinely new data — the receiver has no way, from the sequence number alone, to tell whether an arriving packet is a stale retransmission of old data it already delivered or a brand-new packet from the next window. Concretely: receiver delivers packets `0..N-1`, slides to expect `N..2N-1`; all its ACKs for `0..N-1` are lost; sender times out and retransmits `0..N-1`. If the space had only `N` labels, the retransmitted "old" packet 0 (mod N) is indistinguishable from a new packet that the sender might legitimately send with the same label under a smaller space. Doubling the space to `2N` guarantees the "old window" and "new window" never share a label, so the receiver's buffer-membership check (which window is this sequence number in?) is always unambiguous. This is the origin of the "sequence space ≥ 2N" rule depicted in the figure's overlap zone.

Correctness invariant (informal proof sketch, applies to RDT 3.0 / GBN / SR alike). Define the invariant: at every instant, the set of bytes the receiver has delivered to its application is a prefix of the bytes the sender has been asked to send, and this prefix only grows. Each mechanism exists to preserve exactly this invariant against one specific threat:

  • Checksums ensure a corrupted packet is never mistaken for a valid one (no false extension of the prefix with garbage).
  • Sequence numbers ensure a duplicate (retransmitted) packet is never delivered twice (no false extension by re-delivering old bytes as if new).
  • Timers + retransmission ensure that a lost packet does not stall the prefix forever (liveness: the prefix eventually does grow, given the channel eventually delivers some attempt correctly).
  • Cumulative or per-packet ACKs communicate to the sender exactly how far the prefix has grown, so it knows what may safely be discarded from its buffer and what must be retried.

Together these give both safety (never deliver wrong or duplicate data) and liveness (progress is guaranteed as long as the channel is not permanently and totally broken) — the two halves of a correctness proof for any protocol handling an unreliable channel.

Honest tradeoffs. GBN wastes bandwidth on retransmission under lossy conditions (a single lost packet near the start of a large window discards many good packets) but keeps the receiver trivially simple — valuable in resource-constrained receivers. SR is bandwidth-efficient under loss but requires more complex, stateful buffering on both ends and per-packet timers, which is real implementation complexity. Neither protocol tolerates a channel that reorders packets across different windows or that duplicates packets outside the sequence-number space's disambiguation range — both assume the sequence-number space is chosen large enough per the argument above.

Worked example

Trace a Go-Back-N run with window `N = 4`, sequence space `{0,1,2,3,4}` (5 values = N+1, satisfying the correctness bound), sending 6 data units `D0..D5`. Packet 2 is corrupted in transit; everything else behaves normally. Times are illustrative round numbers, not real network timings.

tEventSender state afterReceiver action
0send P0 (seq 0), start timerbase=0, next=1—
1send P1 (seq 1)base=0, next=2—
2send P2 (seq 2)base=0, next=3—
3send P3 (seq 3), window full (4 sent)base=0, next=4—
4P0 arrives, correctdeliver D0, expected=1, send ACK0
5P1 arrives, correctdeliver D1, expected=2, send ACK1
6P2 arrives corrupteddetect checksum fail, discard, re-send ACK1
7P3 arrives — seq 3 ≠ expected 2discard (out of order), re-send ACK1
8ACK0 receivedbase=1, restart timer—
9ACK1 receivedbase=2, restart timer (base≠next)—
10duplicate ACK1 received (from step 6)base stays 2 (no new info)—
11duplicate ACK1 received (from step 7)base stays 2—
12timer for base=2 expiresresend P2, P3 (entire window base..next-1)—
13P2 (retransmit) arrives, correctdeliver D2, expected=3, send ACK2
14P3 (retransmit) arrives, correctdeliver D3, expected=4, send ACK3
15ACK2 receivedbase=3—
16ACK3 receivedbase=4=next, stop timer—
17window now empty; send P4 (seq 4), P5 (seq 0, wraps)next=6... continues normally ...

Notice the crucial detail at t=7: packet P3 was received correctly (no bit errors) but the receiver still discards it, because GBN receivers never buffer out-of-order data — this is exactly the inefficiency that Selective Repeat eliminates. Re-running the identical scenario with SR instead: at t=7 the receiver would buffer P3 and send ACK3 individually; then only P2's timer fires at t=12, the sender resends P2 alone, and upon its arrival the receiver immediately delivers the buffered D2 followed by the already-buffered D3 without needing P3 retransmitted at all — one fewer packet crosses the network compared to GBN's two retransmissions.

Common pitfalls & traps

PitfallWhy it happensFix
Using 1-bit sequence numbers with a pipelined (windowed) protocolStop-and-wait's 2-value space is sufized only for one outstanding packet; pipelining needs `N+1` (GBN) or `2N` (SR) valuesSize the sequence space to the derived bound, not just "enough for the window"
Forgetting to re-ACK duplicates in GBN/RDT 2.2Assuming only new, in-order packets need any responseAlways ACK (with old cumulative number) on out-of-order/duplicate receipt so the sender's timer logic and buffer management work
Treating NAKs as requiredEarly designs (RDT 2.0/2.1) use them, but they are redundant given sequence numbersRDT 2.2-style ACK-only designs (as TCP uses) are strictly simpler and equally correct
Restarting GBN's single timer incorrectlyRestarting on every ACK instead of only when `base` advances and `base ≠ nextseqnum`Stop the timer entirely when the window is empty; otherwise restart for the new oldest unACKed packet
SR receiver failing to re-buffer/ignore truly-old duplicate packetsWithout a defined "old window" range, a stale retransmission looks like valid new dataExplicitly check whether an arriving sequence number falls in the previous window and, if so, ACK but do not re-deliver
Assuming a bigger window always helpsUnder high loss, large GBN windows amplify wasted retransmissionSize `N` near the bandwidth-delay product; beyond that it only helps throughput on loss-free links

Pioneers & history

The formal treatment of reliable transfer over unreliable channels grew out of the same intellectual lineage as Transport-Layer Services and Multiplexing: the need, in the ARPANET and its successors, to move from best-effort packet delivery to guaranteed byte streams. Sliding-window techniques were first rigorously analyzed for reliable data link protocols (e.g., HDLC-style link control) before being adopted wholesale into the transport layer for TCP.

Origins · 1 minVinton G. Cerf and how this was discovered
VG
Vinton G. Cerf
born 1943 · Co-designed the TCP/IP protocol suite, embedding sliding-window reliable transfer as a core transport-layer mechanism

History
1970
s — sliding-window flow/error control formalized for data-link protocols (ARPANET IMP-IMP links, later HDLC).
1974
— Cerf and Kahn publish the internetworking architecture that becomes TCP/IP, requiring end-to-end reliability atop an unreliable network layer.
1981
— RFC 793 specifies TCP's cumulative acknowledgment and retransmission-timeout mechanisms, a Go-Back-N-flavored design with later selective-acknowledgment extensions.
1988
— Van Jacobson's congestion-control work extends the reliability machinery with adaptive timers and window dynamics.
1996
— RFC 2018 formalizes TCP Selective Acknowledgments (SACK), bringing Selective-Repeat-style precision to a fundamentally Go-Back-N-based protocol.

Where this matters

TCP is the flagship real-world application: its cumulative acknowledgment scheme is essentially Go-Back-N, later augmented with SACK (RFC 2018) to approximate Selective Repeat's efficiency without abandoning cumulative ACKs entirely — a hybrid explored further in TCP Connection Management. Reliable link-layer protocols (HDLC, some Wi-Fi retransmission schemes, Bluetooth's ARQ) use sliding-window ACK/retransmit logic directly at the link layer. Reliable multicast and file-transfer protocols over UDP (building on the gaps left by UDP: Connectionless Transport) reimplement RDT-style sequence numbers and ACK/timeout logic in the application layer, since UDP itself provides none of it. Storage and replication systems (distributed logs, consensus protocols) borrow the same acknowledgment-and-retry vocabulary to guarantee delivery across unreliable network partitions, and QUIC's loss-recovery and packet-number scheme is a modern, more granular descendant of Selective Repeat's per-packet ACK philosophy.

Ask the tutor: How does TCP's SACK option change the sequence-number and buffering analysis compared to plain Go-Back-N?

Why not just make the channel itself reliable instead of building reliability at the transport layer?

Physical channels (radio, copper, fiber) inherently suffer noise, interference, and congestion-driven buffer overflow at intermediate routers — some loss and corruption is unavoidable at the physical and network layers. Building perfect reliability into every link would be expensive and often impossible (e.g., a temporarily disconnected wireless link cannot avoid losing in-flight frames). The end-to-end argument, a foundational networking design principle, says that some correctness guarantees can only be fully enforced at the endpoints where the actual semantic requirement (this application byte stream must be complete and ordered) is known. So the network layer is deliberately left best-effort (UDP: Connectionless Transport exposes this directly), and reliability is added once, correctly, at the transport layer — rather than redundantly and imperfectly at every hop.

Does UDP provide any of these reliability mechanisms?

No. UDP provides only a checksum (optional in IPv4) for corruption detection, multiplexing via port numbers, and a minimal header — it discards corrupted datagrams silently and never retransmits, acknowledges, or reorders. Everything described in this article — ACKs, sequence numbers, timers, windows — is entirely absent from UDP and must be implemented by the application if reliability is desired. This is precisely why protocols like TFTP, RTP-based reliable extensions, and QUIC (which runs atop UDP) reimplement RDT-style logic themselves: UDP is the intentionally bare unreliable channel this entire article assumes as the starting point.

Is TCP exactly Go-Back-N, or exactly Selective Repeat?

Neither exactly. TCP's baseline behavior — cumulative acknowledgments, a single retransmission timer per connection (per unacknowledged region), and the possibility of retransmitting more than strictly necessary — is architecturally Go-Back-N-like. But TCP with the Selective Acknowledgment (SACK) option, negotiated at connection setup, lets the receiver report exactly which out-of-order segments it has buffered, letting the sender retransmit only genuinely missing segments — much closer to Selective Repeat's efficiency. Most modern TCP stacks negotiate SACK by default, so real deployed TCP is a hybrid: GBN-style cumulative ACKs as the fallback, SR-style precision when SACK is available.

Why does Selective Repeat need twice the window size in sequence-number space, but Go-Back-N only needs one more than the window size?

The difference comes from what state the receiver keeps. GBN's receiver only remembers a single number (the next expected sequence number), so the only ambiguity to avoid is between "the packet I'm about to accept" and "a stale duplicate of the packet right before it" — one extra value suffices. SR's receiver keeps an entire window's worth of buffered, individually-tracked packets, so it must distinguish an entire window of new sequence numbers from an entire window of old (possibly-retransmitted) ones. Two non-overlapping windows require a sequence space at least twice the window size, which is exactly the `2N` bound derived in the complexity discussion above.

What happens if the window size N exceeds the bandwidth-delay product?

Once `N` is large enough that the pipe is already saturated (utilization has hit 1), further increasing `N` gives no additional throughput on a loss-free link — the extra window capacity just sits unused because the sender is already sending as fast as the link allows. Worse, under nonzero loss, a larger window in Go-Back-N means a single lost packet forces retransmission of more already-correctly-received data, actively hurting effective throughput. This is one reason real TCP implementations dynamically size their window (via TCP Flow Control and congestion control) rather than fixing it at some large constant.

Can reordering alone (with no loss or corruption) break Go-Back-N or Selective Repeat?

Not if the sequence-number space is sized correctly and reordering is confined within reasonable bounds (packets don't arrive so late that their sequence number has already wrapped and been reused). GBN's receiver simply discards anything that isn't exactly the next expected packet, so extreme reordering just triggers extra (harmless, if wasteful) retransmissions rather than incorrect delivery. SR's receiver tolerates substantial reordering gracefully by buffering, delivering in order only once gaps are filled. The real requirement, proven in the sequence-number-sizing argument, is that reordering delay never lets an old packet with a reused sequence number be confused with a genuinely new one.

Why do timers need to be set carefully rather than just "long enough"?

A timer set too short causes spurious retransmissions — the sender resends a packet whose ACK simply hasn't arrived yet, wasting bandwidth and, in GBN, potentially discarding correctly-received later packets unnecessarily at the receiver. A timer set too long delays legitimate loss recovery, stalling the delivered-data prefix and hurting throughput and interactivity. Real protocols (as elaborated further in TCP Connection Management) estimate the timeout adaptively from measured round-trip-time samples plus a variance term, rather than using any fixed constant, precisely to balance these two failure modes.

Before this

Where this leads