Free — no signup required

TCP Advanced: Flow Control and Congestion Control

3 min read

Managing Traffic Flow

TCP does not just guarantee delivery — it also manages how fast data is sent. Without rate management, a fast sender could overwhelm a slow receiver, or a network link could become so congested that packets are dropped for everyone. TCP solves these two distinct problems with two distinct mechanisms.

Flow Control — Protecting the Receiver
Imagine throwing tennis balls to a partner. If they can only catch five at a time, throwing fifty at once guarantees most will hit the floor. TCP uses a receive window (advertised in every TCP header) to tell the sender exactly how much buffer space the receiver has available right now. The sender must not transmit more unacknowledged data than this window allows.

Originally, the window size field was 16 bits, capping it at 65,535 bytes — far too small for modern gigabit networks where the round-trip time alone means you need megabytes of data in flight to keep the pipe full. Window Scaling (negotiated during the handshake via a TCP option) allows both sides to agree on a multiplier, enabling window sizes up to 1 gigabyte. Without window scaling, a high-bandwidth, high-latency link (like a satellite connection) would be severely underutilized.

Congestion Control — Protecting the Network
What if the receiver has plenty of buffer space, but the routers between sender and receiver are overwhelmed? TCP infers congestion from packet loss (a dropped packet means some router's queue overflowed) and adjusts its sending rate accordingly.

The sending rate is governed by the congestion window (cwnd). TCP starts conservatively with a small cwnd and grows it over time:

  • Slow Start: cwnd doubles every round-trip time (RTT) until it hits a threshold or detects loss. Despite the name, this is actually exponential growth.
  • Congestion Avoidance: After the threshold, cwnd grows linearly (one packet per RTT) until loss is detected.
  • On loss: cwnd is reduced, and the process begins again.

Two major algorithms implement this logic differently:

  • CUBIC (default on Linux): Grows the window aggressively using a cubic function, backs off sharply on loss, then recovers quickly. Optimized for high-bandwidth networks.
  • BBR (Bottleneck Bandwidth and RTT), developed by Google: Instead of reacting to packet loss, BBR continuously models the actual bottleneck bandwidth and minimum RTT of the path, and tries to send at exactly the rate the network can sustain. BBR performs significantly better on lossy links (like mobile networks) where packet loss does not always indicate congestion.

Linux exposes the active congestion control algorithm and the list of available ones as a kernel parameter, and switching to BBR is a one-line runtime change on a 4.9+ kernel — no application code changes required, since congestion control lives entirely in the kernel's TCP stack.

Key Point: Flow control and congestion control are often confused. Flow control is a signal from the receiver saying "slow down, I'm full." Congestion control is the sender inferring from packet loss that the network is full. They operate independently and simultaneously.

This is one of 18 chapters

Every chapter is free — Kubernetes, Terraform, SRE, distributed systems, and more — with fast daily review built in.

Sign up free