尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

TCP拥塞控制与流量控制:从rwnd、cwnd到CUBIC的实操排查指南

TCP拥塞控制与流量控制:从rwnd、cwnd到CUBIC的实操排查指南 简介本资源为高级计算机网络与网络通信技术课程第7章「拥塞控制与流量控制」的配套课件面向高校计算机、通信工程专业学生及备考网络方向的学习者帮助系统掌握拥塞控制与流量控制的核心原理与算法。内容围绕拥塞产生条件、开环与闭环控制、缓冲区预分配、分组丢弃法、定额拥塞控制、抑制信息包法及限制输出队列长度等策略展开并深入讲解TCP慢启动、拥塞避免、快速重传与快速恢复算法以及TCP友好拥塞控制与随机早期检测RED机制配有直接死锁、重装死锁等实例分析。资源包共1个pptx文件约1.02MB以幻灯片形式呈现80页课程内容结构清晰、图文并茂便于课堂讲授与课后复习。目前已有97人学习适合需要梳理拥塞控制知识框架、对照算法细节与典型实例查漏补缺的读者。1. 拥塞控制与流量控制一份 80 页课件背后真正要讲清的两件事很多人第一次翻到「拥塞控制与流量控制」这一章会下意识把这两个词当成一回事毕竟都跟「别发太快」有关。但真到抓包、调参、排查线上吞吐上不去的时候你会发现它们根本是两个层面的问题流量控制是点对点的解决的是「接收方来不及收」靠的是滑动窗口和接收窗口 rwnd拥塞控制是端到端的解决的是「网络中间扛不住」靠的是拥塞窗口 cwnd 和一整套 AIMD、慢启动、快重传快恢复的机制。一份 80 页的课件真正值钱的部分不是那些状态机图而是把「发送方到底按谁的限制发」这件事讲透——发送窗口 min(rwnd, cwnd)。这篇笔记就顺着这个标题把课件里最容易讲糊的地方拆成能复现、能验证、能踩坑的实操路径适合正在学计算机网络、准备期末或 408以及需要把 TCP 行为讲给团队听的工程师。2. 流量控制滑动窗口到底在滑动什么流量控制的核心一句话接收方通过 TCP 首部里的窗口字段告诉发送方「我还能收多少字节」。这个字段是 16 位最大 65535 字节所以高带宽时延积链路上必须靠窗口扩大选项Window Scaling撑开否则吞吐会被死死卡住。课件里通常只画一个滑动窗口示意图但真正要理解的是三个指针发送已确认、发送未确认、可发送边界以及接收方那个 rwnd 是怎么随应用层读取速度动态变化的。2.1 滑动窗口的四个边界与 rwnd 的实时性发送方维护的窗口可以拆成四段已发送且已确认、已发送未确认、可发送但未发送、不可发送。接收方回送的 ACK 里带的 rwnd决定的是「可发送但未发送」这一段的上限。这里有个容易被忽略的点rwnd 是接收方当前剩余缓冲不是固定值。应用层读得慢rwnd 就缩读得快rwnd 就涨。所以你会看到同一个连接里窗口字段一直在变这不是玄学是接收缓冲在实时反馈。零窗口是流量控制里最经典的场景。接收方缓冲满了回一个 rwnd0 的 ACK发送方必须停发然后启动持续计时器persist timer周期性发零窗口探测防止死锁——因为窗口更新的 ACK 如果丢了双方就会一直等下去。这个探测报文只带 1 字节数据收到后接收方重新通告窗口。2.2 用 ss 和 tcpdump 看窗口的真实变化光看图不够得看真实连接。Linux 上用ss能直接看到发送和接收队列配合tcpdump抓窗口字段就能把课件里的静态图变成动态过程。# 查看当前 TCP 连接的发送/接收队列和窗口相关状态 ss -tin state established ( dport :80 or sport :80 ) # 抓包只看 TCP 窗口字段变化-S 显示绝对序列号便于对齐 sudo tcpdump -i eth0 -nn -S tcp port 80 and (tcp[tcpflags] tcp-ack ! 0)ss -tin里的skmem一行会显示rb接收缓冲和tb发送缓冲的用量rcv_space是接收方通告的窗口上限。tcpdump抓到的每个 ACK 报文win后面的数字就是当时的 rwnd。把这两个对照着看你就能确认「接收方读得慢 → rwnd 缩小 → 发送方被限流」这条链路。参数上注意-S用绝对序列号方便你算窗口滑动了多少不加-S是相对序列号看趋势可以算具体字节数容易乱。2.3 窗口扩大选项65535 不够用的时候高带宽时延积链路比如 100ms RTT、1Gbps带宽时延积是 12.5MB16 位窗口最大 64KB吞吐直接被压到 64KB/0.1s 5.2Mbps连百分之一都跑不到。窗口扩大选项在三次握手时协商一个移位因子0~14把窗口字段左移最大能到 2^30 左右。这个选项只在 SYN 和 SYN-ACK 里出现连接建立后就不能改了。# 查看系统是否开启窗口扩大以及相关缓冲上限 sysctl net.ipv4.tcp_window_scaling sysctl net.ipv4.tcp_rmem sysctl net.ipv4.tcp_wmemtcp_window_scaling为 1 表示开启默认就是开的。tcp_rmem和tcp_wmem三个值分别是 min、default、max接收缓冲 max 太小会限制 rwnd 上限进而限制窗口扩大能发挥的作用。常见做法是把 max 调到几十 MB具体看你的带宽时延积。这里有个坑窗口扩大因子是握手时定的如果你中途改了tcp_rmem已建立的连接不会变得重连才生效。3. 拥塞控制从慢启动到 CUBIC 的落地观察拥塞控制和流量控制最大的区别是rwnd 是别人告诉你的cwnd 是你自己猜的。发送方没有任何字段能直接读到网络中间队列有多满只能靠 ACK 到达的节奏、丢包、RTT 变化来推断。这就是为什么拥塞控制算法这么多——Reno、NewReno、CUBIC、BBR本质都是对「网络什么时候算拥塞」的不同假设。课件里通常重点讲 Reno 的四阶段但生产环境里 Linux 默认早就是 CUBIC 了得知道两者差别在哪。3.1 慢启动、拥塞避免、快重传、快恢复的边界条件慢启动cwnd 从 1 个 MSS 开始每收到一个 ACK 就加 1 MSS效果是指数增长直到 cwnd 达到慢启动阈值 ssthresh。拥塞避免cwnd 超过 ssthresh 后每个 RTT 只加 1 MSS线性增长。这两个阶段的分界就是 ssthresh初始值通常设成一个很大的数第一次丢包后才被赋值为当时 cwnd 的一半。快重传的触发条件是收到 3 个重复 ACK不等超时就直接重传丢失的段。快恢复是在快重传之后把 ssthresh 设为 cwnd 的一半cwnd 设为 ssthresh有的实现是 ssthresh3然后进入拥塞避免。超时重传则更狠ssthresh 减半cwnd 直接回到 1重新慢启动。这个区别是考试高频点也是理解「为什么丢包恢复后吞吐掉得不一样」的关键。3.2 用 ss 看 cwnd 和拥塞算法用 ip route 换算法Linux 上ss -ti能直接看到每个连接的 cwnd、ssthresh、rtt、retrans 等拥塞相关字段这是把课件理论对上真实连接最快的方式。# 查看连接级拥塞信息关注 cwnd、ssthresh、rtt、retrans ss -ti state established ( dport :443 or sport :443 ) # 查看当前系统默认拥塞控制算法 sysctl net.ipv4.tcp_congestion_control # 查看内核支持哪些算法 sysctl net.ipv4.tcp_available_congestion_control # 临时切换成 CUBIC 或 BBR需内核支持 sudo sysctl -w net.ipv4.tcp_congestion_controlcubicss -ti输出里cwnd:后面是当前拥塞窗口单位是 MSS 数ssthresh:是慢启动阈值rtt:是平滑 RTTretrans:是重传计数。如果你看到 cwnd 长时间卡在一个小值、retrans 一直涨基本就是链路在丢包拥塞控制在压着发。切换算法用sysctl -w是全局的只影响新连接要对单个连接生效得在应用层用setsockopt的TCP_CONGESTION选项这个课件一般不讲但实际调优常用。3.3 CUBIC 和 Reno 的差别为什么默认换了Reno 的拥塞窗口是线性的长肥管道高带宽高时延上恢复太慢。CUBIC 把窗口增长改成三次函数离上次拥塞点越远增长越快越近增长越慢整体更平滑在高带宽链路上吞吐明显更好。BBR 则完全不看丢包而是建模瓶颈带宽和最小 RTT在有一定丢包的链路上优势明显但和 CUBIC 共存的公平性一直有争议。# 对比同一连接在不同算法下的 cwnd 增长可写脚本定时采样 for i in $(seq 1 20); do ss -ti state established ( dport :443 ) | grep -o cwnd:[0-9]* sleep 1 done这段脚本每秒采一次 cwnd跑 20 秒你就能看到窗口增长的形状。CUBIC 前期涨得快接近拥塞点时变缓Reno 是稳定线性。参数上注意采样间隔要小于 RTT 的若干倍才有意义间隔太大只能看趋势。这个对比方法比死记公式直观得多也是我一般给学生演示时用的手段。4. 避坑与排查拥塞控制里最容易翻车的五个点4.1 把 rwnd 和 cwnd 搞混调优方向全错现象吞吐上不去有人去调tcp_rmem加大接收缓冲结果没变化。原因瓶颈在 cwnd 不在 rwnd接收方窗口一直很大是网络在丢包压着发送方。解决先用ss -ti看 cwnd 和 retrans如果 cwnd 小且 retrans 涨是拥塞问题调接收缓冲没用如果 rwnd 小才是接收方读得慢。两个窗口取 min得先确认哪个是短板。4.2 窗口扩大没生效吞吐卡在 64KB 附近现象高时延链路上吞吐怎么都上不去抓包看窗口字段最大就 65535。原因窗口扩大选项没协商成功可能是中间设备改写了 SYN或者一端没开。解决抓三次握手报文看 SYN 和 SYN-ACK 里有没有 Window Scale 选项因子是多少。如果一端没发检查net.ipv4.tcp_window_scaling。注意这个选项握手后不能改必须重连。4.3 快重传没触发一直等超时现象丢了一个段连接卡了很久才恢复抓包看到重复 ACK 不到 3 个。原因丢包位置在窗口末尾后面没有足够多的段来产生重复 ACK快重传条件凑不齐。解决这是快重传的固有局限NewReno 用部分 ACK 缓解但根本还是靠 SACK选择性确认。检查net.ipv4.tcp_sack是否为 1开了之后接收方能告诉发送方具体缺哪段恢复更快。4.4 切换拥塞算法后老连接没变现象sysctl -w换了算法但现有连接吞吐没变化。原因拥塞算法是连接建立时绑定的改全局默认只影响新连接。解决要影响已有连接得在应用层用setsockopt设TCP_CONGESTION或者重启应用重建连接。这个坑在线上调优时特别常见改完以为生效了其实老连接还在跑旧算法。4.5 零窗口探测被防火墙拦掉现象接收方缓冲满发了零窗口之后连接就死了双方都不动。原因零窗口探测报文很小有些中间设备或防火墙会丢弃这种「空」报文导致窗口更新永远传不过去。解决抓包确认探测报文有没有发出去、有没有被回。如果是中间设备问题只能调整设备策略应用层能做的是尽量让接收方及时读走数据别让窗口真的归零。5. 把课件变成能验证的实验三个进阶技巧学完这一章最怕的就是「图都看懂了一到真实连接就懵」。我的习惯是每讲一个机制就设计一个能观测它的最小实验。第一个技巧是用tc人为制造丢包和时延观察 cwnd 怎么反应。比如在回环口上加 5% 丢包然后跑一个长连接用ss -ti采样 cwnd你会看到 CUBIC 在丢包后窗口掉一半再慢慢爬这个曲线比任何图都直观。# 在 eth0 上模拟 100ms 时延和 2% 丢包观察拥塞窗口行为 sudo tc qdisc add dev eth0 root netem delay 100ms loss 2% # 实验结束后清除规则 sudo tc qdisc del dev eth0 rootnetem的delay和loss是最常用的两个参数delay影响 RTT 进而影响窗口增长速度loss直接触发拥塞控制。注意实验要在测试环境做别在生产网卡上加规则。第二个技巧是对比 SACK 开关前后的恢复速度把tcp_sack关掉再跑同样的丢包场景你会发现恢复慢很多这就把「SACK 为什么重要」讲实了。第三个技巧是画一张自己的对照表把流量控制和拥塞控制在每个维度上列清楚这比背定义有用得多。维度流量控制拥塞控制作用范围点对点发送方-接收方端到端发送方-整个网络反馈来源接收方通告的 rwndACK 节奏、丢包、RTT 推断核心变量rwndcwnd典型机制滑动窗口、零窗口探测慢启动、拥塞避免、快重传快恢复失控后果接收方缓冲溢出网络中间队列溢出、全局拥塞崩溃这张表我一般让学生自己填一遍填不出来的格子就是没理解的地方。最后说个我自己的教训早年调一个跨机房同步任务吞吐一直上不去我第一反应是加接收缓冲折腾半天没效果后来ss -ti一看 cwnd 卡在 10 左右、retrans 一直涨才知道是中间链路丢包拥塞控制在压着。从那以后我养成了一个习惯——任何吞吐问题先看 cwnd 和 retrans再看 rwnd顺序反了就是白费功夫。希望帮到你。本文还有配套的精品资源点击获取
返回列表