How TCP Congestion Control Actually Works
Try the interactive lab for this articleTake the quiz (6 questions · ~6 min)When people say a connection is slow, they usually mean one of several different things at once. The application may be sending tiny writes. The receiver may be advertising a small window. A Wi-Fi hop may be dropping frames. A router queue may be bloated. Or the sender may simply be obeying TCP congestion control and refusing to put more bytes in flight than the path seems able to handle.
That last part is one of the most important mechanisms on the internet and one of the least well understood outside networking circles. TCP congestion control is the reason a fast sender does not just blast packets until every router melts. It is also the reason one flow can take a while to reach full speed, why one lost packet can cut throughput sharply, why latency rises before users complain, and why modern algorithms like CUBIC and BBR produce visibly different behaviour on the same path.
The hard part is that congestion control is not one bit in a header or one formula. It is a control loop. The sender watches indirect signals such as acknowledgments, delay, explicit congestion marks, and loss, then adjusts how much unacknowledged data it allows into the network. That loop sits on top of an internet path full of queues, bottlenecks, middleboxes, radio links, and competing flows.
This article walks through the mechanism in detail: why congestion control exists, how cwnd and the ACK clock work, why slow start is exponential, how fast retransmit and recovery fit in, how CUBIC differs from Reno, what BBR is trying to model, where ECN and AQM help, and what operators actually measure when diagnosing a path. The focus is TCP, but the control ideas are broad enough that they still shape QUIC and other modern transports.
1. Congestion Control Exists Because the Internet Can Collapse Under Load
The need for congestion control was not hypothetical. In the mid-1980s parts of the early internet experienced congestion collapse: more offered load produced less useful throughput. Senders retransmitted aggressively because missing acknowledgments looked like loss. Routers filled with duplicate data. Queues grew. Latency exploded. Goodput, meaning useful application data delivered, fell toward zero.
The key lesson was brutal. A reliable transport cannot treat every failure as a local glitch and simply retry harder. If the network is already congested, extra retransmissions make the congestion worse.
Van Jacobson's 1988 work introduced the core control ideas that still define modern TCP behaviour:
- probe for available capacity gradually
- use ACKs as a timing signal
- interpret loss as a congestion signal
- reduce sending rate when congestion is inferred
- grow again cautiously after conditions improve
Without those rules, every fast sender would act like a queue-filling denial-of-service generator during stress.
This history also explains why congestion control is fundamentally different from flow control. Flow control protects the receiver from being overrun. Congestion control protects the path between sender and receiver from overload.
2. cwnd Is the Sender's Guess About the Path, Not the Receiver
The most important variable in classic TCP congestion control is the congestion window, usually written cwnd. It is the sender's estimate of how much data may safely be in flight without causing or worsening congestion.
The sender is constrained by two windows:
flight_size <= min(rwnd, cwnd)Where:
rwndis the advertised receive window from the receivercwndis the sender's congestion windowflight_sizeis unacknowledged data currently in the network
If the receiver says it can only buffer 64 KB, rwnd wins. If the receiver can accept 16 MB but the network looks fragile, cwnd wins.
That distinction matters because engineers often stare at a high-bandwidth path and wonder why throughput is low even though the receiver is healthy. The answer can be that the sender's congestion controller has not yet allowed enough bytes into flight.
cwnd is not a direct measurement. Routers do not send a field saying "you may now have 73,216 bytes in flight." The sender infers safe behaviour from what comes back:
- ACK pacing
- duplicate ACKs
- retransmission timer expiry
- Selective ACK information
- ECN marks if enabled
- sometimes RTT inflation, depending on algorithm
So congestion control is always an inference problem. The sender is trying to reason about a remote queueing system it cannot see directly.
3. The ACK Clock Is What Makes TCP Feel Self-Timed
One of TCP's most elegant ideas is that the returning ACK stream acts like a clock. When packets are delivered and acknowledged, the ACKs pace the sender's next transmissions.
Suppose a sender has cwnd = 10 MSS. It can inject about ten Maximum Segment Sizes' worth of data into the path. As ACKs come back, slots reopen. Those ACKs effectively say:
- some earlier data left the network path or reached the receiver
- you may send more without growing in-flight bytes beyond the current window
This creates a self-clocking effect. When the path drains smoothly, ACKs return smoothly. When the path stalls or a queue builds badly, ACK timing changes and the sender's behaviour shifts.
This is why burst shape matters so much. A sender that ignores pacing and dumps a whole window at once can create microbursts that overflow shallow buffers even if the average sending rate looks acceptable. Modern stacks therefore combine congestion control with pacing. The window still limits how much may be in flight, but the sender spaces transmissions over time rather than treating "allowed to send 40 KB" as "send all 40 KB right now."
The ACK clock is also why reverse-path problems can distort forward throughput. If ACKs are delayed, bunched, or dropped, the sender's timing signal gets noisier.
4. Slow Start Is Exponential Growth, Not Gentle Growth
A new connection does not know the path capacity. It has to start somewhere.
Classic TCP begins with a small initial congestion window, historically 1 MSS and in modern systems often closer to 10 MSS. From there it enters slow start.
The name is misleading. Slow start is not slow in the ordinary sense. It grows the window quickly. Each acknowledgment increases cwnd, which leads to roughly exponential growth by round trip:
10 MSS -> 20 MSS -> 40 MSS -> 80 MSS -> 160 MSSA rough mental model is:
- send one window
- receive ACKs for that window
- use those ACKs to allow roughly another window's worth of growth
If the path can handle the increase, startup is efficient. If it cannot, a queue forms and loss or marking eventually appears.
Why not jump straight to a very large window? Because the sender has no idea whether the path is a short fibre path inside a datacentre, a congested DSL link in rural Portugal, or a 5G path through a busy urban cell in Warsaw. Starting large would be guessing with real infrastructure risk.
Slow start therefore trades some startup delay for safety. The sender learns capacity by probing upward.
5. ssthresh Marks the Point Where Growth Must Become Conservative
Exponential growth cannot continue forever. At some point, doubling each RTT is too aggressive. TCP therefore uses a threshold called ssthresh, the slow start threshold.
Below ssthresh, the sender uses slow start behaviour. Above it, the sender switches to congestion avoidance, where growth becomes additive rather than exponential.
In rough terms:
- below
ssthresh: grow fast to discover usable capacity - above
ssthresh: grow cautiously to avoid repeated overshoot
If congestion is inferred, stacks typically reduce ssthresh, often to about half the current effective window, and then continue from a safer point.
This is important because most paths have a useful operating region near the bottleneck bandwidth-delay product, but not much headroom beyond it. If a sender keeps doubling through that region, it does not gracefully find the limit. It overshoots, fills queues, and drops packets.
ssthresh is not magical truth. It is remembered history about where growth should stop being reckless.
6. Congestion Avoidance Usually Means Additive Increase
Once TCP exits slow start, classic algorithms grow cwnd more slowly. Reno-style additive increase aims for roughly one extra MSS per RTT.
A common expression of the update is:
cwnd += MSS * MSS / cwndApplied per ACK, this approximates a one-MSS increase over a full RTT's worth of acknowledgments.
That may sound tiny, but it is deliberate. Near the likely path limit, aggressive growth mostly creates queueing and loss. Cautious linear growth lets the sender keep probing for extra capacity while reducing the chance of catastrophic overshoot.
This is the additive increase part of AIMD:
- AI: additive increase while the path appears healthy
- MD: multiplicative decrease when congestion is inferred
AIMD matters because it tends to converge toward a shareable equilibrium when multiple flows compete. It is not perfect fairness, especially with different RTTs and different algorithms, but it is much better than a world where every flow keeps doubling forever.
7. Loss Is a Congestion Signal in Classic TCP, Even When Reality Is Messier
Traditional TCP assumes that packet loss usually means congestion. The logic is simple:
- routers have finite queues
- queues overflow under sustained pressure
- dropped packets reveal that offered load exceeded path capacity
That assumption worked well on wired networks where corruption was rare and queue overflow was the main reason for missing packets.
Modern networks are messier.
Loss can also come from:
- Wi-Fi interference and retransmission failure
- cellular radio transitions
- flaky access equipment
- policing or shaping devices
- transient path changes
- tunnel encapsulation edge cases
Even so, classic loss-based TCP still uses loss as its core feedback signal because it is visible and unambiguous. A missing packet is strong evidence that something went wrong.
The drawback is that by the time loss appears, a queue has often already filled. That means the path is not merely busy. It is usually already operating in a latency-worse region. This is one reason delay and ECN-aware algorithms became so interesting later.
8. Duplicate ACKs Let TCP React Before the Retransmission Timer Fires
If TCP had to wait for a full retransmission timeout on every lost packet, recovery would be painfully slow. Instead it watches for duplicate ACKs.
Consider this sequence:
Sender transmits:
seq=1000 len=1000
seq=2000 len=1000
seq=3000 len=1000
seq=4000 len=1000
Packet with seq=2000 is lost.The receiver gets 1000, then later 3000 and 4000, but cannot advance the cumulative ACK because bytes starting at 2000 are missing. It therefore sends repeated acknowledgments for the same next expected byte:
ACK=2000
ACK=2000
ACK=2000After three duplicate ACKs, classic TCP treats this as strong evidence that one segment was lost but the path is still delivering later packets. That triggers fast retransmit.
This is much better than waiting hundreds of milliseconds or seconds for a timer. The sender retransmits the missing segment immediately and starts recovery logic.
The reason duplicate ACKs are useful is subtle. They carry two facts at once:
- a gap exists
- later data still made progress through the path
That suggests local loss or reordering, not total path silence.
9. Fast Recovery Tries Not to Collapse the Whole Pipe for One Loss Event
After fast retransmit, TCP usually enters fast recovery rather than resetting all the way to a tiny startup window.
In classic form, the logic is roughly:
- infer one loss from duplicate ACKs
- retransmit the missing segment
- reduce
ssthresh, often to about half the current window - reduce the effective sending rate, but keep enough data in flight to use the path
The intuition is important. If duplicate ACKs are still arriving, packets are still moving. The network is congested enough to lose something, but not dead. Resetting all the way to 1 MSS would waste capacity and stretch recovery time.
Fast recovery therefore tries to preserve some pipeline occupancy while reacting to congestion. NewReno and later refinements improved this logic further, especially when multiple losses occur within one window.
Without recovery refinements, large transfers on real networks would spend too much time oscillating between overreaction and underutilisation.
10. SACK Fixed a Serious Blind Spot in Cumulative ACK-Only TCP
Original TCP acknowledgments are cumulative. They say what contiguous byte range has arrived from the left edge onward. That is simple, but it hides detail when several holes exist.
Selective Acknowledgment, or SACK, lets the receiver tell the sender which higher byte ranges arrived out of order. That gives the sender a more precise picture of what is missing.
Without SACK, if several packets in one window are lost, the sender may have to infer and repair them inefficiently. With SACK, it can target retransmission much more accurately.
This matters directly for congestion control because recovery quality affects how long the sender stays below useful throughput after a loss event. Better information means faster, less wasteful recovery.
In modern internet TCP, SACK support is so normal that people forget how much worse loss recovery was without it.
11. Retransmission Timeout Is the Expensive Fallback Signal
Duplicate ACKs help when some traffic is still flowing. But sometimes the sender receives no useful feedback at all. Then the retransmission timer, RTO, becomes the fallback.
If data is outstanding and an ACK does not arrive before the timer expires:
- the sender retransmits
- the event is treated as strong congestion evidence
- the congestion window is cut much more severely than in fast recovery
This is expensive because RTO recovery usually means the sender has gone long enough without feedback that the path looks badly impaired or the window was too small to generate duplicate ACK evidence.
Timeout-based recovery therefore hurts throughput more and takes longer to rebuild from than fast recovery.
This is why later TCP work put so much emphasis on better loss detection heuristics such as RACK and Tail Loss Probe ideas. The goal is to infer loss earlier from timing patterns so fewer cases fall all the way back to blunt timeout behaviour.
12. RTT Estimation Shapes Recovery and Startup More Than Many Engineers Realise
Congestion control is not only about how much data is in flight. Timing matters too.
TCP continuously estimates round-trip time and smoothed variation. Those estimates affect:
- retransmission timeout
- some pacing decisions
- startup interpretation
- detection of unusual path inflation
The classic Jacobson/Karels RTT estimator made timeout behaviour much more stable by accounting for variance rather than using a naive fixed timer.
Why is this part of congestion control? Because a sender that times out too early will retransmit unnecessarily and cut its window for no good reason. A sender that times out too late will sit idle waiting while the application sees avoidable stalls.
Timing also interacts with queueing. If RTT climbs far above the path's propagation baseline, that often means queues are filling. Delay-sensitive algorithms use that signal more directly. Even loss-based algorithms cannot ignore it because it changes the meaning of timer-driven recovery.
13. Queueing Is Where Throughput and Latency Start Fighting Each Other
Every bottleneck path has some queue somewhere:
- a router egress queue
- a cable modem scheduler
- a mobile base station buffer
- a home gateway uplink queue
- a virtual switch queue in a cloud host
If the sender's offered rate is below path capacity, the queue stays small. If the offered rate rises above what the bottleneck can drain, packets accumulate.
A little queueing can be useful. It smooths short bursts and keeps the bottleneck busy.
Too much queueing is where trouble begins:
- RTT rises because packets wait in line
- interactivity suffers before throughput necessarily collapses
- once the buffer fills, drops or marks appear
- recovery cuts
cwnd, then the cycle repeats
This is the heart of the latency-throughput tradeoff. A path can look "fast" in terms of bulk throughput while feeling terrible for interactive traffic because the queue is permanently bloated.
The best mental model is not "congestion begins at packet drop." Congestion begins when queueing delay starts becoming operationally painful. Packet drop is often just the later, more dramatic symptom.
14. Bufferbloat Happens When Big Queues Hide Congestion Too Well
Large buffers sound helpful. If small buffers drop packets, maybe huge buffers solve the problem.
In reality, oversized buffers often create bufferbloat. Instead of dropping early and signalling the sender to slow down, the device soaks up far too much data. Throughput may remain high, but latency becomes terrible because packets spend tens or hundreds of milliseconds waiting in queues.
Typical symptoms:
- speed tests look good
- video calls stutter when someone starts an upload
- SSH becomes laggy during a file transfer
- game latency spikes whenever the household backs up photos
Home broadband links historically suffered badly here because consumer equipment often had oversized queues on the slow uplink side.
Congestion control alone does not fully fix bufferbloat, because classic loss-based algorithms only see decisive trouble after the bloated queue finally overflows. By then delay is already bad.
That is why Active Queue Management and ECN became important companions to congestion control.
15. ECN Lets the Network Say "Slow Down" Without Dropping First
Explicit Congestion Notification, ECN, gives routers a way to signal congestion before dropping packets.
The mechanism works across IP and TCP bits:
- IP marks a packet with Congestion Experienced, CE
- the receiver echoes that signal using TCP's ECE flag
- the sender acknowledges the reduction response with CWR
Conceptually the path says: "I am under pressure. Reduce rate, but I did not have to drop this packet to tell you."
This is attractive because it can reduce loss while still giving congestion feedback. That matters for applications where losses are expensive or where avoiding queue overflow improves latency.
ECN is especially useful when combined with AQM systems such as CoDel or PIE that mark or drop based on queue delay behaviour rather than waiting passively for full-buffer overflow.
Adoption took time because every device in the path had to treat the bits correctly. In older networks some middleboxes mangled or ignored ECN. Today support is much better, but it is still not universal enough that engineers can assume every path handles it perfectly.
16. AQM Tries to Keep Queues Short Enough That Delay Stays Civilised
Active Queue Management, AQM, changes how bottleneck devices behave under load. Instead of letting the queue fill until the buffer is full, the device starts marking or dropping earlier based on delay or occupancy trends.
Well-known schemes include:
- RED, historically important but tricky to tune
- CoDel, which targets persistent queueing delay
- PIE, which uses probabilistic control around target delay
- fq_codel, which combines fair queueing with CoDel-style delay management
The point is not to punish senders randomly. The point is to create earlier, cleaner feedback so congestion controllers do not keep driving the path deep into the bloated region.
On consumer broadband equipment, fq_codel and related approaches made a visible difference to ordinary users. Upload saturation stopped destroying latency quite so catastrophically. On managed networks, ECN-marking AQMs can help preserve both responsiveness and throughput.
Congestion control lives in the sender. AQM shapes the bottleneck's feedback quality. The two belong in the same mental picture.
17. CUBIC Became the Default Because Reno Grew Too Slowly on Modern Paths
Reno's linear growth works, but on high-bandwidth, high-latency paths it can be painfully conservative. If a connection loses a packet after filling a large pipe, regaining the old window one MSS per RTT at a time takes too long.
CUBIC addresses this by making window growth a cubic function of time since the last congestion event, centred around the previous maximum window Wmax.
In simplified form:
W(t) = C(t - K)^3 + WmaxWhere K is chosen so the function returns to Wmax at the right time after a multiplicative reduction.
The practical behaviour is easier to understand than the algebra:
- after a loss event, CUBIC reduces the window
- it then grows cautiously as it approaches the previous maximum
- once it passes the old maximum, it probes more aggressively for new capacity
This shape is useful because it avoids slamming immediately back to the exact point where loss occurred, but it also does not crawl upward forever.
That is why CUBIC became the Linux default and remains widely used on internet-facing servers. It fits modern long-fat paths better than pure Reno-style growth.
18. BBR Tries to Model the Path Instead of Waiting for Loss
BBR, Bottleneck Bandwidth and Round-trip propagation time, takes a different approach from classic loss-based control.
Instead of asking "how much window can I push before loss occurs?" it tries to estimate two path properties:
- bottleneck bandwidth, often written BtlBw
- minimum round-trip propagation time, often written RTprop
From those it derives an approximate bandwidth-delay product:
BDP = BtlBw * RTpropThat estimate is then used to decide pacing rate and a suitable amount of in-flight data.
The goal is to operate near the path's true delivery capacity without filling the queue until it overflows. BBR therefore cycles through phases that probe bandwidth, drain built-up queues, cruise near the estimate, and periodically re-measure.
This produces very different behaviour from CUBIC:
- less dependence on packet loss as the primary signal
- stronger emphasis on pacing
- often shallower queues on some paths
- fairness debates when competing with traditional loss-based flows
BBR is not magic. It can mis-estimate, it can interact oddly with policing and shallow buffers, and BBRv1 drew criticism for RTT fairness problems. BBRv2 improved several of those behaviours. But the conceptual shift is important. Congestion control does not have to wait for drops to discover the operating point.
19. Wireless and Mobile Paths Confuse Loss-Based Controllers
Classic TCP was shaped in an era where packet loss strongly implied queue overflow. Wireless links complicate that assumption.
On Wi-Fi or cellular paths, loss can come from:
- radio interference
- fading
- scheduler changes
- link-layer retransmission exhaustion
- handover between cells
- variable modulation and coding rates
If TCP interprets all of that as bottleneck congestion, it may cut cwnd even when the real issue was not queue overflow at all.
Link layers often hide some of this through their own retransmissions. That helps reliability, but it can also inflate RTT and create burstier delivery to TCP. The sender then sees a strange mixture of delay spikes, reordered delivery, and occasional apparent loss.
This is why the same algorithm can feel fine on a fibre path between Frankfurt and Zurich but behave poorly on a train using mobile data through several radio transitions. The transport is seeing different truth.
20. Long Fat Networks Need Large Windows and Good Startup Behaviour
A path with high bandwidth and non-trivial RTT has a large bandwidth-delay product. To fill it efficiently, the sender must have enough data in flight.
Suppose a path can deliver 10 Gbit/s with 40 ms RTT. The BDP is:
10,000,000,000 bits/s * 0.040 s = 400,000,000 bits
400,000,000 / 8 = 50,000,000 bytesThat is about 50 MB in flight.
If cwnd is much smaller than that, the sender cannot fully utilise the path no matter how powerful the application or NIC is. This is why large windows, window scaling, and sensible congestion growth matter so much for high-capacity international links, storage replication, and bulk transfer between regions.
It also explains why startup can dominate short transfers. If the connection never lives long enough to grow near the path's efficient operating point, the theoretical line rate is irrelevant.
21. Fairness Is a Moving Target, Not a Solved Theorem
Congestion control is often presented as if fair sharing emerges automatically. Reality is more complicated.
Fairness depends on:
- which algorithms are competing
- RTT differences
- bottleneck queue policy
- pacing quality
- ECN behaviour
- application behaviour and connection counts
Two identical CUBIC flows with similar RTTs sharing one bottleneck usually behave reasonably. A BBR flow and a Reno flow on the same path may not. A service that opens twenty parallel connections can grab more aggregate capacity than a service using one well-behaved connection.
This matters in production because application design feeds back into congestion behaviour. Download accelerators, sharded clients, and some old browser strategies effectively changed fairness by multiplying competing transport sessions.
The network is not running one clean textbook algorithm in isolation. It is hosting a crowd.
22. Application Behaviour Can Make Congestion Better or Worse
Developers sometimes talk about congestion control as if it belongs entirely to the kernel and therefore lies outside application responsibility.
That is not true. Application choices shape how the controller behaves.
Examples:
Short-lived connections
If an application constantly opens and closes connections, it keeps paying startup cost and rarely benefits from a warm congestion window.
Tiny writes
If the app writes tiny records one by one, it can trigger poor packetisation and less efficient utilisation even before congestion becomes the main issue.
Parallel connection abuse
Opening many connections to the same endpoint may improve one client's throughput at the expense of fairness and aggregate network health.
Bursty release of work
Dumping a large backlog immediately after a pause can create intense startup bursts that stress buffers more than steady paced sending would.
Mixing latency-critical and bulk data
Putting interactive control traffic and huge bulk transfers on the same TCP flow couples them to the same congestion behaviour and head-of-line blocking.
The transport owns the control loop, but applications choose the workload the loop has to control.
23. Congestion Control Is Not the Same as Rate Limiting or QoS
These ideas often get blurred together.
Congestion control is a sender-side adaptive mechanism responding to observed path conditions.
Rate limiting is a policy decision that caps throughput regardless of path health.
QoS and scheduling policies are network-side decisions about which packets get served first or how different classes share capacity.
A corporate WAN might rate-limit a backup job to 50 Mbit/s even if the path is otherwise empty. TCP congestion control still runs inside that limit. A mobile network might schedule traffic classes differently across radio resources. TCP still responds to the resulting delay and loss patterns.
Understanding the distinction matters when debugging. A flat throughput ceiling may come from a policer, not from cwnd. A latency spike may come from queueing under a traffic shaper, not from a receiver issue.
24. Modern Diagnostics Usually Start with ss, Packet Capture, and Queue Clues
When a production TCP flow is slow, engineers often want to know three things quickly:
- what is limiting bytes in flight?
- is the path dropping, marking, or queueing badly?
- which congestion controller is active?
On Linux, ss -ti is often the first useful view. A representative snippet might look like:
cwnd:42 ssthresh:21 rtt:18.4/2.1 mss:1448 bytes_acked:912344
pacing_rate 86.1Mbps delivery_rate 79.4Mbps unacked:42 retrans:1/3That is not a full explanation, but it immediately exposes important state:
- current
cwnd - whether slow start has likely ended
- RTT and variance
- retransmissions
- pacing and measured delivery rate
Packet captures help answer different questions:
- are duplicate ACKs appearing?
- are ECN bits set?
- is loss clustered?
- are retransmissions timer-based or fast?
- is the ACK stream bunched strangely?
Queueing clues often come from outside TCP itself:
- elevated RTT under load
- modem or router queue statistics
- fq_codel counters
- interface drops on the bottleneck device
No single tool tells the whole story because congestion control is a loop across endpoints and network elements.
25. Datacentre TCP and Internet TCP Do Not Live in the Same World
Inside a datacentre, RTTs are tiny, paths are controlled, and ECN-based schemes such as DCTCP become attractive. Operators may care deeply about microsecond-scale queueing because it affects tail latency for RPCs.
On the public internet, paths are heterogeneous, RTTs vary wildly, and endpoints do not control the bottleneck devices. Loss-based and model-based controllers have to tolerate far more uncertainty.
This difference matters because engineers sometimes import one environment's instincts into the other. Advice that is correct for a low-latency ECN-marked fabric in a hyperscale facility may be useless on a residential broadband link in Naples.
TCP is one protocol family, but the operating assumptions differ sharply by environment.
26. Why One Dropped Packet Can Still Hurt So Much
People often ask why a single lost packet can damage throughput so badly on an otherwise fast connection.
Several reasons stack together:
- the sender interprets loss as congestion evidence
cwndis reduced, sometimes sharply- recovery consumes time and may stall later bytes in the stream
- the sender then has to probe upward again
- if the path RTT is large, each probe cycle costs more wall-clock time
On a short path with low RTT, recovery may be quick enough that users barely notice. On a high-BDP path, the same relative reduction can mean a large absolute throughput penalty until the window rebuilds.
This is why clean optics, stable radio links, and sensible queue management still matter even in an era of sophisticated transport stacks. Congestion control can adapt. It cannot repeal path quality.
27. What Engineers Usually Get Wrong About Congestion Control
Several mistakes come up repeatedly.
Confusing receive window with congestion window
A large rwnd does not mean the sender is free to use the whole path.
Assuming high bandwidth means high throughput automatically
Without enough in-flight data, line rate is theoretical.
Treating loss as the first sign of trouble
Latency often got bad long before the first drop.
Ignoring application burst shape
The controller sees the traffic pattern your application creates.
Assuming one algorithm name explains everything
"It uses CUBIC" is not a diagnosis. Queue policy, RTT, loss pattern, pacing, and competing flows still matter.
Believing congestion control is obsolete because links are faster now
Faster links do not remove bottlenecks. They often make queueing pathologies more dramatic when edge devices or radio links are the true constraint.
28. What TCP Congestion Control Is Really Doing
If you reduce congestion control to "TCP slows down when packets drop," you miss the real mechanism.
TCP congestion control is continuously trying to answer three questions:
- how much data can I keep in flight right now?
- how quickly should I inject new data into the path?
- what signal tells me I have gone too far?
Classic answers came from ACK pacing and packet loss. Newer answers incorporate better timing, explicit marks, pacing, and path models. But the job is the same. The sender is trying to keep the bottleneck busy without driving the network into destructive queueing and collapse.
That is why congestion control still matters so much. It is not just a protocol detail. It is the internet's compromise between selfish senders and shared infrastructure.
When it works well, users experience a network that feels stable and predictably fast. When it works badly, every application above it ends up blaming "the network" for symptoms that began with one control loop misreading a crowded path.