Retransmission and Timers
Reliable delivery means noticing loss and resending. TCP has two ways to notice: a timeout (nothing came back in time) and duplicate ACKs (something came back, but it is telling you the same thing repeatedly). This lesson covers how TCP sets that timeout, why it must adapt as the network changes, and the two related timing behaviors that most often surprise people in practice - Nagle's algorithm and delayed ACK.
Estimating RTT: SRTT and RTTVAR
TCP cannot use a fixed timeout - a LAN round trip is under a millisecond, a transcontinental one is well over 100ms, and either can change mid-connection. Instead it maintains a running estimate, updated on every non-retransmitted ACK using an exponentially weighted moving average (the Jacobson/Karels algorithm):
SRTT (smoothed RTT) tracks the average round trip. RTTVAR tracks how much
recent samples have jumped around it. Standard constants are alpha = 1/8 and
beta = 1/4 - chosen as powers of two so the whole computation can be done
with integer shifts instead of floating-point division.
Computing RTO
The retransmission timeout is built from both numbers, not just the average:
The 4 * RTTVAR term matters: a path with a stable RTT can use a tight
timeout, but a path whose RTT jitters needs slack, or ordinary variance keeps
tripping the timer into false retransmits. RFC 6298 also floors RTO at 1
second and requires it to at least double on every consecutive timeout
(exponential backoff), capping only at an upper bound (commonly 60s).
Concrete example. Suppose SRTT = 100ms, RTTVAR = 20ms:
A sample of 150ms arrives (higher than average, path got a bit slower):
RTO grew because the variance grew, even though SRTT barely moved. If the next several samples come back near 106ms, RTTVAR decays back down and RTO tightens again. This is the mechanism, not just the average, that lets TCP shrink its timeout on a quiet, stable path and stretch it on a jittery one.
Karn's algorithm: why retransmits don't count
If a segment is retransmitted, which ACK does a later reply belong to - the
original transmission or the retransmit? There is no way to tell from the ACK
alone (this is the retransmission ambiguity problem). Using the wrong one
poisons the RTT estimate in either direction: crediting a slow retransmit as
if it were fast makes RTO shrink dangerously; crediting a fast retransmit's
ACK to the slow original inflates it needlessly.
Karn's algorithm resolves this by refusing to guess: never use a sample
from a segment that was retransmitted to update SRTT/RTTVAR at all. Only
clean, non-retransmitted round trips feed the estimator. (A companion rule,
Karn's backoff, keeps the exponentially-backed-off RTO from a timeout in place
across the very next segment, rather than snapping straight back down.)
Two ways to detect loss
| Signal | What it means | Reaction |
|---|---|---|
| RTO expires | no ACK at all arrived in time - possibly heavy congestion or a dead path | retransmit, reset ssthresh, restart from slow start (see Congestion Control) |
| 3 duplicate ACKs | later data is arriving and being ACKed, but one segment is still missing | fast retransmit the missing segment immediately, without waiting for RTO |
A duplicate ACK means the receiver got something out of order and is re-announcing the same "next expected byte." One duplicate can just be reordering; three duplicates (the original ACK plus three repeats) is strong enough evidence of an actual gap that TCP retransmits right away rather than waiting out the full timeout - this is fast retransmit, and it is almost always faster than waiting for RTO to expire.
Delayed ACK
Sending a bare ACK for every single segment is wasteful, so a receiver may hold an ACK briefly (commonly up to 200ms, or "wait for one more segment") to see if it can piggyback the ACK on outgoing data or fold two ACKs into one. This reduces small-packet overhead on request/response and bulk transfers alike.
Nagle's algorithm
Nagle's algorithm targets a different waste: an application that writes data one byte (or a few bytes) at a time - an interactive terminal, for example - would otherwise generate a flood of tiny segments. Nagle's rule: if there is already unacknowledged data in flight, buffer further small writes until either a full segment's worth accumulates or the outstanding data is ACKed. It trades a little latency for far fewer segments.
Put the two together and they can deadlock on latency. The sender, following
Nagle, will not send its next small write until the previous one is ACKed. The
receiver, following delayed ACK, is in no hurry to send that ACK and waits to
see if more data or a response is coming. Neither side acts, so every write
stalls for the full delayed-ACK timeout (often ~200ms) before the next byte
moves - a very visible, very reproducible latency bug on request/response
traffic with small writes. The standard fix is to disable Nagle on the socket
(TCP_NODELAY) for latency-sensitive, small-message workloads such as RPC and
interactive protocols, leaving delayed ACK alone.
TCP_NODELAY is the socket option most engineers reach for after chasing an
unexplained ~40ms or ~200ms floor on small request/response calls. If disabling
it fixes the latency, Nagle and delayed ACK were very likely the cause.