当应用变慢时,TCP在告诉你什么
网络性能问题的直觉排查往往停留在ICMP丢包或带宽利用率上,但真正导致应用吞吐量下降的,常常是传输层内部的行为——重传、乱序和零窗口通告。Wireshark作为最通用的报文分析工具,能够将这些隐蔽的TCP事件转化为可视化的统计和标记。本文不讨论抽象的协议理论,而是直接给出可操作的过滤命令、图表用法和判断逻辑,让你在面对抓包文件时能准确回答“到底哪里卡住了”。
重传:最直接的吞吐量杀手
如何用Wireshark捕获重传
Wireshark通过分析序列号与ACK的关系自动标记重传,对应的显示过滤器为:tcp.analysis.retransmission。打开抓包文件后,直接输入该过滤条件,所有被判定为重传的报文会高亮显示。需要注意的是,Wireshark的重传判断基于“同一序列号且至少有一个ACK确认过之前的字节”,因此对于快速重传(由三次重复ACK触发),它会额外标记为tcp.analysis.fast_retransmission。你可以用tcp.analysis.retransmission or tcp.analysis.fast_retransmission合并查看两种重传。
重传的根因与影响
重传意味着发送方未能及时收到ACK,最常见的三个原因:实际物理丢包(如链路误码)、接收端处理延迟导致ACK积压(但很少引发重传)、网络拥塞导致中间路由器主动丢弃。在Wireshark中,重传率(重传报文数/总报文数)超过0.5%通常需要警惕。重传会直接增加RTT,因为发送方必须等待RTO超时或收到重复ACK才能补发。更严重的是,TCP拥塞控制算法(如CUBIC)在检测到丢包后会大幅降低拥塞窗口,导致吞吐量骤降。你可以通过“统计 → 捕获文件属性”查看总重传比例,或者用“统计 → TCP流 → 选择一条流,勾选‘显示重传’”来逐流分析。
结合时间序列图定位重传模式
仅凭过滤列表很难看清重传的时序关系。建议使用“统计 → IO图表”,在Y轴设置为“包数”,添加一条过滤为tcp.analysis.retransmission,单位调整为毫秒,观察重传是否集中在某个时间段。如果重传均匀分布,可能为随机丢包;如果集中在T=0附近且之后吞吐量长时间低迷,通常是握手阶段重传或窗口问题。另一个实用工具是“统计 → TCP流 → 时间流图”,它能展示序列号随时间的变化,重传会在图上表现为水平或回退的线段。
乱序:被忽略的拥塞触发源
乱序在Wireshark中的标识
TCP乱序指接收方收到的数据段序列号不连续(即中间缺少某些字节),但并未真正丢失——只是延迟到达。Wireshark使用tcp.analysis.out_of_order标记这类报文。与之相关的还有tcp.analysis.lost_segment,后者实际是Wireshark推测的“可能丢包”(基于序列号不连续但无重传标记)。乱序本身并不直接导致性能问题,但它会触发接收方生成重复ACK(通常三次),进而诱导发送方快速重传并误判为拥塞,导致不必要的窗口收缩。
乱序的常见成因
在数据中心内部,ECMP多路径转发、LAG链路负载分担、或者内核RPS/RFS导致的不同CPU处理顺序,都可能使数据包到达顺序与发送顺序不一致。跨运营商网络的多路径传输(如MP-TCP的备选路径)也常见乱序。判断方法:在Wireshark中过滤tcp.analysis.out_of_order,如果乱序报文数占总报文数超过1%,且重传率同时偏高(但实际物理丢包率很低),那么乱序大概率是主因。
验证乱序是否影响性能
打开一条TCP流,右键“Follow TCP Stream”,然后切换到“统计 → TCP流 → 内容”。查看序列号曲线是否出现锯齿或回退,如果乱序过多,会导致接收方的滑动窗口频繁停滞。更精确的方法是使用“统计 → TCP流 → 时间序列图(Stevens)”,将X轴设为时间,Y轴设为序列号,观察是否有重复的序列号点(即重传)出现在乱序段之后。若发现乱序发生在重传之前,说明乱序触发了不必要的重传,需要在上游调整负载均衡策略——比如关闭LAG的packet-spray模式改为每个流固定一个链路。
Zero Window:接收端成为瓶颈
零窗口通告的过滤与解读
TCP接收端通过窗口字段告知发送方自己还剩多少缓冲区。当接收端应用程序读取数据过慢时,缓冲区满,接收端会发送窗口大小为0的ACK。Wireshark过滤tcp.window_size == 0可找到所有零窗口通告。更智能的方法是使用tcp.analysis.zero_window,它同时包括了零窗口以及窗口更新为0后的第一次非零窗口(即zero window probe)。注意:零窗口本身不是错误,而是接收端正常的流控行为。但频繁出现零窗口且持续秒级,说明接收端处理能力严重不足。
案例分析:一次慢SQL背后的TCP零窗口
有一次,某数据库从应用服务器拉取大量数据,网络带宽充足,但实际传输速率只有几百KB/s。抓包后发现发送方向窗口字段一直很小,经常出现0。进一步检查接收端(数据库)的CPU使用率,发现SQL解析进程导致内核TCP应用程序未能及时通过recv()读取数据,缓冲区积压。通过tcp.analysis.zero_window过滤后观察时间分布,每次零窗口持续200~300毫秒,发送方在此期间持续发送窗口探测(Wireshark会标记为tcp.analysis.zero_window_probe),这些探测报文本身增加了带宽消耗但并不携带有效数据。最终通过优化SQL查询、增加recv缓冲区大小(修改net.core.rmem_max)解决。
区分零窗口与窗口满
窗口大小即使不为0,但如果持续处于很低水平(如几百字节),同样会限制吞吐量。Wireshark可以计算平均窗口大小:选中一条流,点击“统计 → TCP流 → 正向/反向吞吐量”,查看“平均窗口大小(字节)”。若远小于发送窗口上限(如64KB),则说明接收端处理缓慢。配合tcp.analysis.ack_lost_segment(实际是丢包相关,但这里可以提醒)谨慎使用,因为零窗口下发送方会停止传输,但重传不会出现,因为窗口探测定时探测,不触发重传计时器。
三者的关联与综合排查框架
使用Expert Info快速定位
Wireshark的Expert Info(分析 → 专家信息)按严重程度(错误、警告、注意、对话)汇总所有TCP异常事件。打开后按“组”查看,重传通常标记为“Note”或“Warning”,零窗口标记为“Note”,乱序标记为“Note”。如果错误级别的事件很少而警告/注意很多,说明网络存在亚健康。按照“严重性”排序,优先解决红色或黄色条目。
制定排查优先级
建议按以下顺序排查:
1. 检查是否有大量重传(>1%)。若有,再判断是随机丢包还是拥塞导致。随机丢包可通过增大TCP缓冲区或调整RTO最小值缓解;拥塞导致的则需优化网络拓扑或启用BBR拥塞控制。
2. 如果重传不多但吞吐量仍低,检查乱序比例。乱序过多应调整链路负载均衡策略,在发送端启用tcp_reordering参数(sysctl net.ipv4.tcp_reordering)可容忍更高乱序。
3. 若前两者正常,最后检查零窗口。零窗口经常和慢速应用绑定,需要优化接收端应用程序I/O模型或增大套接字接收缓冲区。
这三个问题有时同时存在:例如,乱序触发伪重传,重传导致带宽下降,进而使接收端因数据积压出现零窗口。Wireshark的时间相关性分析(右键选择“时间”列)能帮你理清先后顺序。
验证调整效果
每一次参数修改后,应重新抓包对比同一流。使用Wireshark的“统计 → 捕获文件属性”对比调整前后的重传率、平均RTT、窗口大小均值。也可以导出CSV数据,在Excel中绘制累积重传数量曲线。注意,不同TCP拥塞控制算法(BBR vs CUBIC)对重传和乱序的敏感性不同,BBR在乱序高时性能下降更明显,因此若使用BBR务必优先排除乱序。
总结
Wireshark分析TCP重传、乱序与零窗口,核心在于用对过滤器、看懂时间序列图、关联应用层行为。不要过分依赖单一指标,零窗口不一定是应用问题,乱序可能是网络设计问题,重传可能来自虚假拥塞。通过本文提供的命令和分析框架,你可以快速从海量报文中剥离出真正导致性能瓶颈的因素。下次抓包时,不妨先输入tcp.analysis.flags查看所有异常事件的汇总,再针对性深入。网络优化的起点,是准确诊断。
延伸阅读
