Reliability and Congestion Avoidance over UDP
Source: https://gafferongames.com/post/reliability_ordering_and_congestion_avoidance_over_udp/ Author: Glenn Fiedler | Date: 2008-10-20
Why not TCP reliability
Action games need steady 10–30 pps of time-sensitive data (inputs, transforms, physics). TCP reliable-ordered stream stops and waits on loss → stalls newer packets. Need: send at steady rate; be notified which packets arrived; decide application-level how to handle loss.
Sequence numbers
Identify packets with increasing sequence numbers in header:
[protocol id][sequence](data...)
Acks
Include ack of most recent received remote sequence in each outgoing packet. Problem: clumped receives, asymmetric rates, lost ack packets.
Reliable acks via bitfield (not TCP sliding window)
Never resend the same sequence number. If data must be resent, compose a new packet with a new sequence. Because holes are allowed, one ack number is insufficient.
Header:
[protocol id][sequence][ack][ack bitfield 32 bits]
Ack bitfield bit n means "also ack (ack - n)". Always carry 33 acks (ack + 32 previous) with redundancy so one lost ack packet does not lose the signal. On receive, update remote sequence if newer; process bitfield to mark packets acked.
Detecting loss
If no ack within ~1 second (at ≤30 pps with redundant acks), treat as lost. Certainty of receipt is high; certainty of loss is probabilistic. Resent application data needs its own message ids for dedup.
Sequence wrap-around
For 16-bit sequences, compare with wrap-aware "greater than" (half-range rule) so 0,1,2 count as more recent than 65535.
Congestion avoidance
UDP has none; flooding routers buffers packets → multi-second latency. Measure RTT: queue (seq, send time); on ack compute sample; exponentially smooth (~10% toward new sample); discard unacked after max expected RTT (~1s).
Simple binary congestion avoidance: Good mode e.g. 30 pps; Bad mode e.g. 10 pps. If RTT exceeds threshold (e.g. 250ms) → immediately Bad. Return to Good only after sustained good conditions for time t. If drop Good→Bad in under 10s, double t (clamp ~60s). For each 10s in Good, halve t (clamp ~1s). Avoids oscillation; conservatively probes higher rate.
Conclusion
Steady packet stream + delivery notifications + optional app-level resend + basic RTT-driven rate drop.