TCP拥塞控制详解:慢启动、拥塞避免、快重传与快恢复

发布时间:2026/9/23 4:12:31

TCP拥塞控制详解:慢启动、拥塞避免、快重传与快恢复 TCP拥塞控制是运输层最绕、也最容易被问崩的一块内容。不管是期末复习、考研408还是工作中排查网络为什么忽快忽慢最后都会撞上这四个算法慢启动、拥塞避免、快重传、快恢复。很多同学把这几个概念背得滚瓜烂熟但真遇到“cwnd突然减半”“带宽明明很高吞吐却上不去”的时候又完全对不上号。这篇我打算把TCP拥塞控制从原理到实操整个过一遍用具体的数值例子和抓包观察把脉络捋清楚适合正在复习计算机网络基础、备战考研或者工作中想搞懂网络为什么慢的开发运维同学。1. 拥塞控制到底在解决什么问题1.1 拥塞是怎么发生的先说清楚这里说的“拥塞”到底是什么。我们平时上网数据不是直接从发送端飞到接收端的中间要经过一串路由器。每台路由器都有缓存buffer数据包到了之后先在缓存里排队再由转发引擎送往下一跳。如果流量太大路由器缓存被填满后续到达的数据包就只能被丢弃。你可能觉得丢包无所谓TCP本来就有重传机制丢了再传不就行了。问题在于重传会进一步把更多数据引入网络让拥塞更加严重。这就是拥塞崩溃congestion collapse的雏形——大量数据包在途中被丢弃接收端收不到完整数据发送端不断重传网络吞吐量趋近于零。TCP拥塞控制的核心目标就是让发送端在“让更多数据进入网络”和“避免压垮中间路由器”之间找到一个平衡点。这里要区分一个概念拥塞是网络的整体状态不是接收端的个体状态。你可以把网络想象成一条多车道的公路路由器就是收费站。发送端是高速入口接收端是出口。如果所有车都挤进高速收费站的排队缓冲区会溢出后续车辆连进都进不去。TCP拥塞控制就是让每辆车在进入高速前先试探一下现在路上堵不堵我该以多快的速度汇入车流。1.2 流量控制与拥塞控制是两码事这个点几乎每次都会被问。流量控制flow control和拥塞控制congestion control虽然都是控制发送速率但控制的对象完全不同。流量控制是点对点的解决的是“接收方处理不过来”的问题。接收方会在TCP报文段首部写明自己的接收窗口rwndreceiver window告诉发送方“你现在最多再发这么多字节多了我缓存装不下”。它保护的是接收端。拥塞控制是端到端的解决的是“网络中间节点处理不过来”的问题。发送端维护一个拥塞窗口cwndcongestion window通过观测丢包和确认来推测网络的承载能力控制自己注入网络的数据量。它保护的是整个网络路径上的路由器。最终的发送窗口swnd min(rwnd, cwnd)。也就是说发送端能发多少取决于接收端能收多少和网络能承载多少中更小的那个。我在实际排查中见过很多“传大文件到最后越来越慢”的情况就是接收窗口和拥塞窗口没有区分清楚盲目调大了某个参数结果瓶颈压根不在那个位置。1.3 拥塞控制的总框架AIMD整个TCP拥塞控制可以浓缩成四个字AIMD即加性增、乘性减Additive Increase Multiplicative Decrease。没有拥塞时窗口缓慢线性增长加性增检测到拥塞时窗口按比例缩小乘性减。这套思想贯穿了慢启动、拥塞避免、快重传、快恢复四个算法。慢启动负责快速从小窗口爬升到接近可用带宽的位置拥塞避免负责在接近带宽上限后做线性微调快重传负责及时修复单个丢包快恢复负责在丢包后避免吞吐量暴跌。下面我们逐个拆开讲。2. 慢启动和拥塞避免TCP的第一步2.1 慢启动从1开始翻倍增长慢启动Slow Start这个名字有很强的误导性——它其实一点都不慢是“指数增长”。刚开始发送端不知道网络能承受多少数据为了安全起见cwnd初始值设为1个MSSMaximum Segment Size最大报文段长度典型值1460字节。每收到一个确认ACKcwnd就增加1个MSS。注意这里的增长方式。每收到一个ACK就cwnd 1并不是说一个RTT内只能增加一次。假设当前cwnd n在一个RTT内有n个报文段发出它们全部被确认每个ACK都会让cwnd增加1因此一个RTT后cwnd n n 2n。也就是说只要所有报文都顺利被确认每过1个RTTcwnd就翻一倍。我举个例子。假设MSS 1KBRTT 100ms第1个RTTcwnd 1KB发送1个报文段收到1个ACK后cwnd 2KB第2个RTT发送2个报文段收到2个ACK后cwnd 4KB第3个RTT发送4个报文段收到4个ACK后cwnd 8KB第4个RTT发送8个报文段cwnd 16KB四个RTT之后发送速率从1KB/RTT变成16KB/RTT增长非常迅猛。但慢启动不能无限增长下去否则马上就会把网络打爆所以需要一个切换点这就是慢启动阈值ssthreshslow start threshold。2.2 拥塞避免从指数切换到线性当cwnd达到ssthresh时TCP从慢启动切换到拥塞避免Congestion Avoidance。拥塞避免阶段采用加性增每个RTTcwnd只增加1个MSS而不是翻倍。为什么到了这个阶段就“保守”了因为慢启动的指数增长是用来快速探测带宽的但探测到一定程度网络可能已经接近容量上限继续指数增长会瞬间造成拥塞。线性增长则是在每个RTT内缓慢试探一点点额外带宽更加温和。拥塞避免的“避免”不是说你永远遇不到拥塞而是指用一种缓慢试探的方式去逼近网络容量上限尽量避免过度冲击网络。实际上拥塞仍然会发生TCP正是通过“遇到拥塞→减小窗口”来反复逼近网络的真实容量。这就是为什么你能在抓包工具里看到TCP吞吐量的曲线呈锯齿状升一点、撞一次、降下来再升。ssthresh的初始值怎么定不同的操作系统和协议栈实现不一样常见的是初始值设置为一个较大的数例如64KB或更大的缓冲区上限或者在握手阶段根据双方缓冲区大小动态调整。在考试和面试中最常考的不是初始值而是拥塞发生后ssthresh如何动态变化——记住每次发生拥塞ssthresh max(2, cwnd/2)然后根据拥塞类型决定cwnd是归1还是减半。2.3 拥塞判定的两条路径TCP靠什么判断网络拥塞了靠丢包。而丢包在TCP中有两种主要表现第一种是超时重传Timeout Retransmission。发送端发出数据后启动一个计时器如果在RTORetransmission Timeout重传超时时间内没收到确认就认为这个报文段丢了。超时通常意味着网络拥堵得比较严重连ACK都无法正常返回。第二种是收到3个重复ACK3 Dup ACKs。假设发送端发了1、2、3、4、5五个报文段其中3号丢失。接收方能收到4和5但TCP要求按序交付接收方只能缓存4和5然后对最后一个按序到达的报文段2号回复ACK。于是接收方每收到一个乱序报文段就重复发送对2号的确认。当发送端连续收到3个重复ACK即第4个确认2号的ACK时可以推断3号丢失了。超时和3个重复ACK对拥塞严重程度的判断不同。超时说明网络可能已经堵死反馈链路也不畅通重复ACK虽然说明有丢包但后续报文能到达说明网络还能传输数据拥塞程度相对较轻。这两种判定路径触发了不同的拥塞处理策略这正是第3章要展开的内容。3. 快重传与快恢复遇到丢包不认输3.1 快重传不等超时就重传如果没有快重传Fast Retransmit发送端丢了一个报文段后只能干等到超时计时器到期才重传。RTO在最坏情况下可能长达几秒这个等待对吞吐量的打击是致命的尤其在高带宽长距离网络所谓的“长肥网络”上一个RTO时间浪费的数据量是巨大的。快重传的思路很简单接收方收到乱序报文段就立即发送重复ACK不等自己需要发送数据时再捎带确认发送端一旦连续收到3个重复ACK立即重传丢失的报文段不需要等超时计时器到期。注意这里的“3个重复ACK”是一个经验性阈值。1个重复ACK可能只是乱序到达比如网络里报文走不同路径顺序轻微错乱2个重复ACK也有可能是乱序。连续3个重复ACK才说明“后续报文都到了就是中间缺一段”此时判定丢包比较可靠。我在实际抓包时见过很多初学者在Wireshark里看到重传还一脸茫然“这个包明明被回复ACK了呀为什么标记为TCP Retransmission”就是因为没有理解快重传的触发逻辑。Wireshark会在数据包前加“TCP Dup ACK”标记并针对同一序号出现多个Dup ACK的情况提示“Fast Retransmission”。3.2 快恢复避免回到起点传统的TCP Tahoe版本在收到3个重复ACK时和超时处理一样ssthresh cwnd/2cwnd 1重新进入慢启动。这样做虽然安全但代价很大——好不容易把窗口从1涨到几十个MSS一丢包全归零吞吐量断崖式下跌。于是Reno版本引入了快恢复Fast Recovery。思路是既然收到的重复ACK说明网络还能传数据毕竟ACK能回来后面的数据还在流动那就不必回到慢启动的起点。Reno快恢复的基本流程是这样的收到3个重复ACK时ssthresh cwnd / 2然后cwnd ssthresh有些实现是cwnd ssthresh 3×MSS因为已经有3个数据段离开网络相当于给“在途数据”腾一点空间接下来进入拥塞避免阶段cwnd每个RTT增加1个MSS线性增长也就是说丢包后窗口只是减半不是归零。这既响应了拥塞信号比之前保守又不会让吞吐量瞬间崩塌。3.3 Tahoe与Reno算法演进史我把Tahoe和Reno在丢包场景下的行为对比一下场景TahoeReno超时ssthresh cwnd/2cwnd 1慢启动同左3个重复ACKssthresh cwnd/2cwnd 1慢启动ssthresh cwnd/2cwnd ssthresh快恢复拥塞避免Tahoe是最早的完整拥塞控制实现它证明了“拥塞后必须退避”的原则。Reno在Tahoe基础上增加了快恢复减少了不必要的吞吐量损失成为很长时间里Linux等系统的默认算法。但Reno有个毛病如果在一个窗口内丢了多个报文段它只能在每个RTT内恢复一个丢包效率很低。于是后来出现了NewReno改进了对部分确认Partial ACK的处理能在同一窗口内恢复多个丢包。再后来出现了Vegas、Westwood等基于时延或带宽估计的算法但这些在课本上通常不会细讲考试默认还是以Reno/NewReno为主。4. 四个算法如何协同运作完整实例与实战观测4.1 一个完整例子从1 MSS开始到拥塞前面把四个算法拆开讲了但它们在实际运行中是连续切换的。我用一个具体的数值例子来演示完整流程假设MSS 1KBssthresh初始为16KB接收窗口足够大不成为限制。第一阶段慢启动cwnd从1KB开始每个RTT翻倍1 → 2 → 4 → 8 → 16当cwnd 16KB时cwnd ssthresh进入拥塞避免第二阶段拥塞避免cwnd线形增长16 → 17 → 18 → 19 → 20 → 21 → 22 → 23 → 24假设cwnd 24KB时发生超时第三阶段超时后的乘法减小ssthresh cwnd / 2 12KBcwnd 1KB重新进入慢启动第四阶段再次爬坡慢启动1 → 2 → 4 → 8 → 12注意到这里cwnd12KB等于新的ssthresh进入拥塞避免13 → 14假设cwnd 14KB时收到3个重复ACK某个报文段丢失但后续报文到达第五阶段快重传快恢复立即重传丢失报文段ssthresh 14 / 2 7KBcwnd 7KB进入拥塞避免之后继续线性增长8 → 9 → 10 ...这个例子覆盖了全部四个算法。你可能会发现一个规律——拥塞避免阶段的线性增长本质上是在试探网络容量而每一次拥塞事件都会把ssthresh拉低导致后续窗口更难爬高。如果网络持续拥塞窗口就会在低位徘徊吞吐量自然上不去。4.2 用Wireshark实际观察TCP窗口变化理论讲了这么多怎么在实际网络里验证我推荐用Wireshark抓包观察这是最直观的方式。第一步抓包。在Linux服务器上执行tcpdump -i eth0 host 目标IP and tcp port 80 -w tcp_cwnd.pcap抓到足够多条TCP报文后用Wireshark打开。第二步在Wireshark里观察TCP Stream。右键任意一个TCP包 → Follow → TCP Stream可以看完整会话。在Statistics → TCP Stream Graphs → Time-Sequence (Stevens)中可以看到发送序列随时间的变化。如果看到一段段“阶梯式”爬升、然后突然下坠的曲线那就是典型的拥塞窗口变化轨迹。第三步关注标记。Wireshark会自动标记“TCP Dup ACK”和“TCP Retransmission”。如果你看到大量重复ACK后面跟着快重传说明发判断丢包并触发了快恢复。如果你看到RTO超时图上表现为一段长时间空白然后重新发起点说明网络拥塞较严重走了超时重传路径。这里有个需要注意的地方Wireshark直接显示的“Window Size”是接收窗口rwnd不是拥塞窗口cwnd。拥塞窗口是发送端的内部状态抓包时看不见。但你可以通过观察发送速率的变化来反推cwnd的增减如果一段时间内发送方每个RTT发出的字节数翻倍就是慢启动如果每个RTT增加一个MSS的速率就是拥塞避免如果速率突然降一半说明发生了乘法减小。4.3 Linux内核里的拥塞控制开关在Linux上拥塞控制算法是可以通过内核参数切换的。我平时排查网络性能问题时第一步就是确认当前用的是哪个算法。# 查看当前拥塞控制算法 sysctl net.ipv4.tcp_congestion_control # 查看内核支持哪些算法 sysctl net.ipv4.tcp_available_congestion_control常见的输出是cubic或者reno。CentOS/RHEL系列默认通常显示cubic这是Linux的默认算法采用三次函数控制窗口增长在高带宽长距离链路上比Reno表现更好。如果你看到的是reno而你的场景是跨机房大流量传输可以考虑切换算法。开启BBR的方法如下# 在/etc/sysctl.conf中加入 net.ipv4.tcp_congestion_control bbr # 生效 sysctl -p # 验证 sysctl net.ipv4.tcp_congestion_controlBBRBottleneck Bandwidth and RTT是Google提出的算法它不再把丢包当作主要拥塞信号而是根据最大带宽和最小RTT构建网络模型在高速网络和有一定丢包率的环境比如无线网络下表现非常惊艳。但要注意切换BBR后如果链路中还有其他TCP Reno/CUBIC流量可能出现公平性问题——BBR会抢占更多带宽。还有一个容易被忽视的参数sysctl net.ipv4.tcp_slow_start_after_idle默认值是1表示TCP连接空闲一段时间后重新进入慢启动状态。对于长连接交互式请求比如数据库连接池这个机制会让空闲后的第一次请求变慢。很多高并发后端会在初始化脚本里把它设为0避免频繁慢启动。4.4 CUBIC与BBR现代改进方向聊到现代Linux默认的CUBIC毕业设计和面试题里经常让人对比。CUBIC的窗口增长不再依赖RTT的线性反馈而是定义一个以时间为自变量的三次函数。它的大致行为是发生拥塞减窗后先快速恢复到拥塞前的窗口值然后在接近上限时放慢增长速度这样既保证高带宽网络的利用率又减少对队列的冲击。BBR则是另一个维度。传统算法只在“发快了→丢包→减窗”的循环里震荡BBR却试图直接测量瓶颈路由器的带宽和传输时延让发送速率收敛到“刚好填满管道但不排队”的状态。我用BBR跑跨洋大文件传输时吞吐量提升非常明显因为普通算法会被少量随机丢包误判为拥塞不断减窗而BBR不把丢包当唯一天条。不过任何算法都不是银弹。考试的时候还是以Reno为主但如果你去公司面试基础架构岗位能讲清楚CUBIC和BBR的差异是很加分的。5. 常见问题速查与面试考点5.1 实战中频繁踩坑的场景在实际开发和运维中TCP拥塞控制的表现很多时候不是“题型”而是真实的性能瓶颈。我把几个高频问题整理出来第一高带宽但吞吐量上不去。这不是简单的拥塞控制能解决的。先看接收窗口和缓冲区使用netstat -s统计TCP接收缓冲溢出、丢包重传次数再用ss -i查看每个TCP连接的cwnd和RTT。如果确认是窗口限制可以调大net.ipv4.tcp_rmem和net.ipv4.tcp_wmem的范围或者确认双方都开启了TCP Window ScalingRFC 7323否则窗口最大就只有64KB传大文件必然卡在窗口上限。第二无线网络环境频繁掉速。无线链路丢包率高传统拥塞控制会把随机丢包当成拥塞信号动辄减半窗口。这种场景下改启用BBR或者CUBIC都能有效降低误判带来的吞吐量损失。第三长连接空闲后首请求特别慢。这个就是之前提到的tcp_slow_start_after_idle在作怪关闭这个内核参数同时在应用层做连接保活避免连接被中间设备回收。第四流量监控里看到大量重复ACK。这不一定代表拥塞也可能是乱序、网卡多队列导致、中间设备缓存的路径不同。排查时不要只盯着“重复ACK”这一个指标要结合重传数量、RTT变化和丢包率一起看。5.2 面试和考试最常问的几个点把高频考点整理成速查表考前过一遍很有帮助问题关键回答要点慢启动为什么叫“慢”名字有误导实际指数增长从1个MSS起步防止一开始就冲击网络ssthresh什么时候被改拥塞发生时ssthresh cwnd/2初始值由协议栈设定超时和3个重复ACK分别怎么处理超时cwnd1重新慢启动3个重复ACK快重传快恢复cwndssthresh快恢复为什么cwnd不归1因为重复ACK说明数据还能流动说明拥塞相对较轻没必要把窗口清零拥塞避免为什么每次只加1个MSS为了在接近网络容量上限时做“温和试探”避免再次引发拥塞流量控制和拥塞控制的区别流量控制保护接收端rwnd拥塞控制保护网络路径cwnd发送窗口取二者的较小值为什么TCP吞吐曲线是锯齿状AIMD策略加性增长探测带宽乘性减少释放压力周而复始真题里还有一种画图题给你一个初始ssthresh让你画出cwnd随RTT变化的折线。这种题最稳妥的做法是手推一步步的数值先标出慢启动的翻倍点再标出进入拥塞避免后的线性增长最后标出拥塞事件窗口的下坠。我个人在实际操作中的体会是TCP拥塞控制初学觉得算法多、状态复杂但把它当成一套“放探针→看反馈→调窗口”的闭环控制逻辑就好懂了。考试也好、排查故障也好记住一条主线——一切行为都是围绕“探测网络可用带宽并避免压垮网络”来设计的。另外在Linux上做性能调优时不要一上来就改算法先用ss -i和netstat -s定位瓶颈到底在窗口、在丢包还是在接收端再决定动哪个参数。最后再分享一个小技巧自己动手在本地起一个服务用tcpdump抓一次从建连到传输完整的包对照这章讲的四个切换点逐个标记比背十遍课本都管用。
延伸阅读

更多相关文章

2026/9/23 4:12:31

好的wap项目避坑指南:5步搞定版本兼容难题

好的wap项目避坑指南:5步搞定版本兼容难题 版本升级后 API 全变了,你的代码还在报错?别慌,这份 好的wap 实战避坑指南能救你。很多应届生刚入职就踩这个坑,明明照着 官方文档…

2026/9/23 4:12:31

5个避坑指南:一家之鼠原理详解,告别复制代码跑不通

5个避坑指南:一家之鼠原理详解,告别复制代码跑不通 复制来的代码跑不通,报错信息看得头晕,不知道从哪下手调?别慌,这不仅是你的问题,也是很多初级开发者甚至劳务班组负责人在对接后端系统时常见的痛点。今天这篇避坑指南,不讲虚的,直接拆解【一家之…

2026/9/23 5:12:34

GPT与Codex关系解析:从注册到API接入的完整使用教程

1. 从零理解 GPT 与 Codex 的真实关系很多人第一次接触这两个词的时候,脑子里是一团浆糊的。GPT 和 Codex 到底是什么关系?是同一个东西的两个名字,还是两个完全独立的产品?这个问题不搞清楚,后面所有的操作都会走弯路…

2026/9/23 5:12:34

从SKILL.md到脚本:如何把安全审计做成AI Agent技能包

最近半年,AI Agent 圈子里最火的一个词就是 skill。Claude、Codex、OpenCode 这些工具先后都加入了 skill 机制,GitHub 上也冒出来一堆"技能库",里面全是别人写好的 SKILL.md。说实话,一开始我并不太理解这玩意和普通的…

2026/9/23 5:12:34

Chinese-CLIP中文图文检索系统:CPU本地部署实战指南

简介:本资源是一份面向计算机视觉课程学习者与本科生的图文跨模态检索系统实践项目,聚焦Chinese-CLIP模型在中文场景下的实际应用,适用于期末大作业、课程设计及AI入门实战。压缩包共59个文件,含40个Python源码(涵盖ap…

2026/9/23 5:12:34

合同类别最佳实践:5类核心模式选型指南与避坑详解

合同类别最佳实践:5类核心模式选型指南与避坑详解 配置环境就卡半天,往往不是因为你手慢,而是因为你没搞懂底层逻辑。很多团队在微服务架构中处理合同数据时,习惯性地堆砌业务逻辑,导致代码耦合严重,一旦需求变更,改一行代码就得排查三个模块。这种“…

2026/9/23 5:07:33

告别配置噩梦:用Python写个提前还款计算器,性能优化实操

告别配置噩梦:用Python写个提前还款计算器,性能优化实操 装库报错、路径冲突、环境版本打架,是不是每次搞点开发配置环境就卡半天?这种痛苦我懂。其实很多工具类小项目,根本不需要复杂的工程化结构,核心逻辑一旦跑通,剩下的就是 性能优化…

2026/9/22 10:02:42

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介:《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南,面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者,用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/22 9:07:39

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介:这份PPT围绕互联网业务安全托管服务展开,面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者,重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件,包体约30.63MB,以…

2026/9/23 0:01:54

3个实战技巧搞定形式英语:从看教程到跑通性能优化

3个实战技巧搞定形式英语:从看教程到跑通性能优化 看了一堆教程还是不会写项目?别慌,这种“眼高手低”的困境在开发者圈子里太常见了。很多人以为卡点在语法,其实真正拦路虎是缺乏将知识点串联成完整链路的能力。今天咱们不聊虚的,直接拿【形式英语】这…

2026/9/22 16:34:32

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/22 20:01:30

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/22 13:25:41

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码