TCP拥塞控制详解

明明网络很稳定,速度却忽快忽慢?其实TCP一直在暗中和网络讨价还价。

为什么需要拥塞控制

互联网是一种共享资源,许多连接会经过相同的路由器和链路。如果每个发送方都以最大速度不加节制地发送数据,路由器缓冲区就会溢出,导致丢包激增、重传增多,最终可能引发拥塞崩溃,让整个网络的吞吐量急剧下降。拥塞控制正是TCP在这种情况发生之前主动调节发送速度的机制。

拥塞窗口(cwnd)是什么

拥塞窗口表示发送方在收到确认(ACK)之前可以发送的数据量。窗口越大,一次能发送的数据越多,吞吐量也越高,但如果过大,网络就会处理不过来,导致丢包。几乎所有拥塞控制算法,归根结底都是一套如何增大或缩小这个数值的规则。

慢启动:逐步试探网络容量

连接刚建立时,TCP对网络状况一无所知,因此会从很小的窗口开始,每次成功收到确认就将窗口翻倍。这种指数级增长能快速摸索出网络大致能承受的速度,直到发生丢包或达到预设阈值,慢启动结束,进入更谨慎的拥塞避免阶段。

拥塞避免:出现丢包就降速

传统TCP把丢包视为网络拥塞的信号。一旦检测到丢包,就会大幅缩小窗口(通常减半),此后只要不再丢包,就缓慢线性地增大窗口。这种反复缩小又增大的过程,在吞吐量曲线上会呈现出典型的锯齿状形态。

Reno与New Reno:经典的基于丢包的算法

诞生于上世纪90年代的TCP Reno,结合了慢启动、拥塞避免以及快速重传、快速恢复机制,能在丢包后迅速做出反应并恢复。New Reno改进了同一窗口内多个数据包同时丢失时的恢复效率。但在带宽极高的链路上,两者丢包后恢复速度都偏慢,难以充分利用带宽。

Cubic:Linux默认算法,专为高带宽环境优化

Cubic是当前Linux内核的默认拥塞控制算法,它让窗口大小随时间按三次函数(立方)曲线变化。丢包后会先快速恢复到接近之前的水平,随后逐渐放缓增速,并在接近上次丢包点时重新变得谨慎。这种方式让Cubic在高带宽、长延迟的链路上比Reno系算法能更高效地利用带宽。

BBR:直接测量带宽和延迟的新思路

像Reno、Cubic这类基于丢包的算法,只有在发生丢包之后才会做出反应,而这时路由器缓冲区往往已经被填满(这与缓冲区膨胀问题密切相关)。谷歌开发的BBR则不同,它持续实时估算瓶颈链路的真实带宽和近期最短的往返时延,力图在丢包发生之前就维持最优的发送速率。BBR已应用于谷歌自身的多项服务,在Linux内核中也可以选用。

拥塞控制只是TCP可靠性机制的一部分

除了拥塞控制,TCP还依靠序列号、确认应答和重传计时器来保证数据完整、有序地到达。拥塞控制专门解决的是整个网络的公平性和稳定性问题——如果没有它,少数激进的发送方就可能拖垮共享链路上所有人的体验。

你通常能查看正在使用的算法,却很少能真正切换

大多数消费级操作系统和浏览器都不会开放切换拥塞控制算法的选项,因为这一设置在服务器端的影响远大于客户端。Linux服务器通常可以通过内核参数查看并切换Cubic、BBR等可用算法,这也是为什么繁忙网站、视频平台和CDN的运维人员格外关心这个选择。

常见问题

我可以更换设备使用的拥塞控制算法吗?

在Linux系统上可以,内核提供了在Cubic、BBR等算法之间切换的设置。而Windows、macOS以及大多数移动操作系统都不会向普通用户开放这一设置,实际上连接中服务器一端对体感速度的影响通常比客户端更大。

通过UDP传输的视频通话或游戏流量也会受拥塞控制影响吗?

UDP本身并没有内置的拥塞控制机制,因此行为不规范的UDP应用可能在网络拥塞时依然持续发送数据,给共享同一链路的其他用户造成负担。构建在UDP之上的现代协议,例如QUIC,则实现了自己的拥塞控制逻辑,以更负责任的方式运作。