ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Wireshark实战:从抓包到TCP性能分析,一次讲透网络排查思路

Wireshark实战:从抓包到TCP性能分析,一次讲透网络排查思路

1. 从一次“网络慢得离谱”的排查说起

大概两年前,我接手了一个莫名其妙的故障工单。某台服务器上的业务接口偶尔会卡顿几十秒,但又不是每次必现,监控图上看起来像心跳图一样一抖一抖的。常规三板斧——重启服务、清缓存、换网络——全试了一遍,问题依旧。最后实在没招了,我抱着“死马当活马医”的心态,在某台机器上装了Wireshark,抓了一个小时的流量,结果十分钟就定位到了问题:客户端的TCP零窗口通告导致对端发送窗口被打满,数据卡在上层业务缓冲里干等。那一刻我意识到,Wireshark不是“抓包工具”,而是网络世界里的CT机——它不生产流量,只提供影像,但你要是会用,它能看到别的工具完全看不到的东西。

这篇博文我想聊聊我自己在真实环境里使用Wireshark的经验。不是那种“打开软件、选网卡、点开始,然后盯着花花绿绿的列表发呆”的入门水文,而是围绕几个我自己踩过的坑、反复验证过的排查套路,以及几个容易让人误判的细节。如果你是运维、后端开发、网络工程师,或者单纯对“数据包到底长什么样”感到好奇的爱好者,这篇文章应该能帮你少走很多弯路。

先把话放这儿:大多数人学Wireshark,卡住的不是不会用,而是不知道自己要找什么。抓包本身没有意义,从数据里还原出故障的逻辑链条才是意义。所以这篇文章的重点不在于背过滤语法,而在于建立一套“从现象倒推数据、从数据反查代码”的思路。

2. 抓包前的三个关键准备,多数人直接跳过

很多人装完Wireshark就兴冲冲开抓,抓完发现一堆冗余流量淹没目标,或者重要数据包还没来得及看就被缓冲挤掉了。这里有几个我总结出来的、适用于绝大多数场景的准备工作。

2.1 明确“抓什么”与“不抓什么”

Wireshark默认会抓取选定网卡上的所有进出流量。如果你在一台繁忙的服务器上直接开抓而不加过滤,那画面基本就是一屏接一屏的广播包、ARP询问、多播DNS——真正关心的那个TCP流可能被冲得找不着影。

我现在的习惯是:抓包前先想清楚三件事。第一,目标主机是谁(源IP或目的IP)?第二,目标端口是什么(比如MySQL的3306、HTTP的80/443)?第三,是要看握手细节(那就要关注SYN、ACK、FIN这些控制位),还是只看应用层数据(那直接从HTTP、TLS层切入就行)。

想清楚之后,用**捕获过滤器(Capture Filter)**在源头就砍掉不需要的流量,而不是等抓完再慢慢查。这个和显示过滤器(Display Filter)是两码事——前者在抓包期间就生效,抓进来的包就是筛过的;后者只是把已经抓到的包在界面上隐藏起来。注意,捕获过滤器的语法不同于显示过滤器,它是类BPF(Berkeley Packet Filter)的写法,比如:

# 只抓来自或去往192.0.2.10、且端口是443的包 host 192.0.2.10 and tcp port 443 # 抓某个网段的HTTP流量 net 192.0.2.0/24 and tcp port 80

顺手提一个特别容易踩的坑:捕获过滤器的“and”“or”必须用小写,词与词之间用空格分开。你要是写成大写AND,Wireshark会直接报错——没错,它就是这么挑剔。

2.2 选择合适的接口与抓包模式

Wireshark的“接口列表”会列出你机器上的所有网卡,包括虚拟机的虚拟网卡。常见错误是选错了接口——比如你以为在抓“外网流量”,结果抓的是虚拟网卡上的VLAN区间流量,包全被截断了。

第二个问题是混杂模式(Promiscuous Mode)。默认情况下Wireshark只抓发往本机MAC地址的包。如果你想分析交换端口上流经的、并非发给本机的流量,就需要开启混杂模式。但这里要提醒一句:现在的交换机大多隔离了端口之间的流量,你开启混杂模式也未必能抓到别人的包,别指望Wireshark能干“网络探针”的活儿。

第三个准备是环形缓冲。长时间抓包时,内存或磁盘迟早会被写满。我的习惯是:菜单栏的“选项 → 输出”里设置输出文件为多个文件,每个文件大小设为20MB或50MB,并使用环形缓冲(比如保留20个文件)。这样既有足够的回看窗口,也不会把磁盘写爆。

2.3 单包大小与截断策略

Wireshark默认抓取完整数据包(完整帧大小,通常上限为1514字节或更大的巨型帧)。但如果是在高流量环境,或者你只关心TCP头部和少量负载,完全可以设置“限制每个包捕获长度”为128字节或256字节。别小看这个选项——它能显著降低抓包文件体积,关键是多数分析场景根本不需要完整的应用层数据。

我自己在分析NFS或SMB这类大块传输协议时会刻意截断,因为那些协议一上来就是动辄几KB的数据,完整抓下来文件膨胀得很快。分析TCP行为只看头部就够,分析文件内容才需要完整负载。

3. 过滤器不是背出来的:建立属于自己的过滤思维

过滤器是Wireshark的命根子。但我发现很多人在这一步就卡住了——因为他们试图去背一大堆字段名,而不是理解“过滤是对协议的反射”。

3.1 显示过滤器的查词逻辑

显示过滤器(Display Filter)的语法其实特别简单,核心就是“协议名.字段名”的组合,再加比较运算符。不用背,鼠标点开任意一个包的详情区,看到哪行感兴趣,右键 → “作为过滤器应用”,Wireshark会自动生成对应的过滤表达式。

举个例子。你在包列表里看到一个HTTP GET请求,想看看某个IP后续的所有HTTP请求,完全不需查手册。点开那行的HTTP头,右键“作为过滤器应用”里的“选定”就行。它生成的表达式一般是这样的:

http.request.uri contains "/api/v1/order"

这里的“contains”是字符串包含匹配,对大小写敏感;“matches”则支持正则表达式。我特别常用matches来合并多重条件:

tcp.port == 3306 && ip.src_host == 192.0.2.10 || tcp.port == 3306 && ip.dst_host == 192.0.2.10

上述写法有点啰嗦,更优雅的写法是:

tcp.port == 3306 && (ip.src_host == 192.0.2.10 || ip.dst_host == 192.0.2.10)

括号在Wireshark过滤表达式里同样适用,别怕用括号,它能让逻辑清晰很多。

3.2 我常用的几个过滤套路

这些表达式我几乎每天都会用到,这里分享出来:

场景显示过滤器
只看TCP重传包tcp.analysis.retransmission
只看TCP的SYN包(连接请求)tcp.flags.syn == 1 && tcp.flags.ack == 0
只看带有RST标志的包(连接重置)tcp.flags.reset == 1
检查HTTP请求方法http.request.method == "POST"
查看某个时间点的DNS查询dns.flags.response == 0 && dns.qry.name contains "example"
查找大于某一字节数的包frame.len > 1400
过滤出特定TLS版本tls.record.version == 0x0303

这七条基本能覆盖工作里80%的日常规则。说到底,过滤器的意义不在于显得你很专业,而在于帮你把注意力集中到值得看的那几十个包上。

3.3 为什么要“先过滤后分析”

我在培训新人时常说一句话:“抓包是录影,过滤是剪辑,分析才是写影评。”如果你抓了10万条记录,却一上来就滚动浏览,大概率什么都看不出来。先把数据收敛到几十条以内的可疑集合,再逐条看字段,逻辑链才能浮现出来。

MAC地址与IP地址的映射、TCP序列号的连续性、窗口大小的变化——这些关键信息藏在成千上万的包里,过滤就是拿来“降维”的。哪怕你只是加一个tcp.analysis.flags,Wireshark就会把那些疑似异常(重传、乱序、重复ACK、零窗口等)都标出来,眼睛瞬间就轻松了。

4. 完整分析链路:从“看见”到“看懂”的实操案例

接下来我拆解一个真实场景,这是我在测试某个内部系统时完整走过的排查链路。场景是这样的:客户端A访问服务端B上的某个HTTP接口,功能偶发超时,重启服务后恢复正常,过一段时间又复发。我把这个过程从头到尾走一遍,各位可以对照自己的网络排查套路来找差距。

4.1 第零步:抓包并定位会话

先在服务器B(192.0.2.20)上启动抓包,捕获过滤器设为:

host 192.0.2.10 and tcp port 8080

等故障复现之后停止抓包,保存为时间戳命名的pcap文件。打开文件,第一件事不是看包内容,而是用“统计 → 对话”看看到底有哪些TCP连接,以及这些连接分别传输了多大流量、持续时间多长。一次故障对应哪一两个TCP流,这一步会直接把你带到事发现场,而不是在汪洋大海里捞针。

确认了可疑流之后,右键 → “追踪流 → TCP流”,把整个会话的字节流按发送方向还原。这一步能看到应用层有没有明显异常,比如半截HTTP响应、重复的请求头等。

4.2 第一步:检查三次握手与TCP参数

以某个具体连接为例,展开它的SYN、SYN-ACK、ACK三个包,重点看这五样东西:

  • 初始序列号(ISN)是否合理递增;
  • 窗口缩放因子(Window Scale)是否一致,如果客户端支持缩放而服务端不支持,会严重影响吞吐;
  • 最大段大小(MSS),两端是否一致(不一致时取小值);
  • 选择性确认(SACK)是否启用;
  • 时间戳选项是否存在,特别是Windows时间戳的开启与否。

在我这个案例里,握手阶段一切正常,所以问题不在建连阶段。

4.3 第二步:追踪传输阶段的时间线

关闭“追踪TCP流”视图,回到包列表。现在给我的过滤器加上tcp.analysis.retransmission || tcp.analysis.zero_window || tcp.analysis.duplicate_ack,然后把时间显示格式调成“自上一帧显示的时间差”。

这时画风突变——我发现在某个时间点,客户端连续发出了数个Dup ACK,服务端紧接着重传了一个数据段,然后客户端窗口显示为0(Zero Window),持续了将近3秒才恢复。这就是典型的“发送方传出去一堆数据,接收方缓冲区满,TCP流控踩了刹车”。

零窗口不是网络坏了,而是说接收端的应用层没有及时取走缓冲里的数据。于是回头翻服务端的业务日志,发现那段时间正好在同步大批量数据库结果集,由于一个嵌套循环处理过慢,导致TCP缓冲区被填满。问题不是出在网络,而是出在应用层处理速度跟不上。

4.4 第三步:纵向对比,识别是否偶发

为了确认这个现象不是偶然,我回到“统计 → IO图表”,把过滤器设置为tcp.analysis.zero_window,并按时间轴画图。故障期间的零窗口事件形成了一个明显的波峰,而在正常时段,这项指标为零。这样一来,网络层背锅基本被排除,后续优化代码逻辑即可收工。

这个案例最值得学习的一点是:Wireshark并不会直接告诉你“哪里坏了”,它只会告诉你“这里发生了什么事”。零窗口、重传、乱序都只是生理指标,背后可能是网络丢包、网卡驱动、服务端负载、应用层GC停顿、防火墙策略等一连串因素。不要看到重传就骂网络,要顺着链路去验证。

5. HTTPS抓包:解密的前提与边界

现在网络流量里HTTPS占比远超HTTP,这就带来一个现实问题:包是抓到了,但内容全是密文。Wireshark本身并不对抗加密,它只是提供了几个合理的解密入口。

5.1 利用SSLKEYLOGFILE环境变量

Wireshark官方推荐的做法是通过环境变量SSLKEYLOGFILE读取浏览器或支持该特性的应用程序导出的会话密钥。以Firefox为例,在环境变量或配置文件中设置路径后,浏览器会用预主密钥(pre-master secret)写入这个文件。然后在Wireshark的“编辑 → 首选项 → Protocols → TLS”里添加这个密钥文件路径,重新加载抓包文件,原来一团黑的TLS数据就变成明文了。

我自己经常抓本机的HTTPS流量排查前端接口问题,用这种方法非常方便。不过要特别提醒:不要再继续深信它能解密所有TLS流量——TLS 1.3引入了一些前向保密机制,部分密钥导出依赖会话票据,虽然主流浏览器还是会配合SSLKEYLOGFILE,但不保证所有客户端都能成功。

5.2 解密后的重点检查项

成功解密之后,我一般先看三件事:

  • HTTP响应状态码分布,有没有大量5xx或重定向;
  • 请求头里的缓存控制字段(Cache-Control),判断是不是缓存策略导致客户端拿到旧数据;
  • 响应体的大小及传输分块,看有没有Content-Length与实际传输不一致导致的挂起。

这类问题在HTTPS里特别隐蔽,因为从网络层面看,TCP正常、TLS握手正常、GET请求也正常发出,如果不解密,你永远不知道服务端返回的是一张错误页面。

5.3 不要用Wireshark去解“不该解”的密

Wireshark的解密能力是给应用调试和网络排障用的,比如你是后端开发者,想排查自己运行的测试环境。拿它去分析别人的加密流量,既涉及合规风险,技术上也没那么万能。工具是中性的,但用法的边界是使用者自己把握的。

6. 排查TCP性能问题时,别忽略这些隐藏指标

很多文章都讲TCP三次握手、四次挥手,但真正排障时,更关键的是中间传输过程中的性能和异常指标。

6.1 重传(Retransmission)不等于丢包

Wireshark标记重传的机制是:如果同一个序列号在超时时间内再次出现,就标记为重传。在网络拥塞、链路质量差的场景里,重传确实常见;但还有一种可能是性能问题引起的“伪重传”——比如接收端一直在零窗口,发送端超时重发,也会被标记。因此别一看到红色标记就判断丢包,要结合上下文来看。

6.2 乱序(Out-of-Order)与重复ACK的判别

如果收到较大序列号的包先到,较小的后到,Wireshark会标记Out-of-Order。这在网络路径不对称、负载均衡跨链路场景里非常常见。乱序不一定是故障,但严重的乱序会触发快速重传机制,白白吃掉吞吐。我一般会启动“统计 → TCP流图 → 时间序列(Stevens)”,用可视化方式看序列号曲线的平滑度。如果曲线像锯齿一样上下跳动,说明乱序问题突出。

6.3 窗口大小与吞吐量计算

TCP的接收窗口大小决定了发送端一次能发多少数据而不被确认。最小窗口到零窗口,往往意味着接收端处理不过来。要估算某条连接的理论吞吐上限,可以用这个简化公式:

吞吐量 ≈ 接收窗口大小 / 往返时延(RTT)

举例:如果窗口大小为65535字节,RTT是100毫秒,那理论吞吐上限大约只有5.24Mbps。假如业务要求10Mbps以上的传输速率,那无论上游带宽多大,TCP流控都会成为瓶颈。排查这类问题时,Wireshark可以在“TCP流图 → 窗口缩放”里直接看到窗口随时间的变化曲线。

6.4 小心时间戳字段的“副作用”

TCP时间戳选项本意是辅助RTT计算和保护序列号回绕(PAWS机制)。但某些设备的驱动或中间盒如果对时间戳处理不当,可能引起会话被重置。若排查时发现大量tcp.analysis.ack_rtt异常,怀疑是与时间戳相关,可以尝试抓包时在“高级参数”里关闭TCP时间戳选项做对比实验。

7. Wireshark之外的“隐形帮手”:命令行工具与效率技巧

别以为Wireshark只活在图形界面里。真正处理大规模pcap文件时,命令行工具才是效率神器。

7.1 tshark:无头模式下的过滤机器

tshark是Wireshark同源引擎的命令行版本。它的优势在于可以无界面地跑在服务器或容器里,配合管道和脚本做自动化分析。常用的用法:

# 从pcap中读取,只看HTTP请求URI并输出 tshark -r capture.pcap -Y "http.request" -T fields -e http.request.method -e http.request.uri # 实时抓包30秒,写到文件 tshark -i eth0 -a duration:30 -w halfmin.pcap # 统计TCP流的字节数 tshark -r capture.pcap -q -z conv,tcp

参数-Y表示显示过滤器,-T fields指定用表格形式输出字段,-e后面跟具体字段名。写脚本循环处理多个pcap时,tshark比图形界面快出好几个数量级。

7.2 Wireshark配套小工具:editcap、mergecap、capinfos

这三个工具我称他们为“pcap三兄弟”。

  • capinfos可以快速查看一个抓包文件的详细信息,包括包数、起始时间、持续时间。排查时先跑一句capinfos xxx.pcap,比打开Wireshark快得多。
  • editcap可以按时间范围切割、按包号过滤、甚至可以去掉重复包。比如想单独提取故障前后的10秒数据,用editcap -A 'YYYY-MM-DD HH:MM:SS' -B 'YYYY-MM-DD HH:MM:SS'就行。
  • mergecap用来合并多个抓包文件。多服务器同时抓包时,最后用mergecap把所有文件合并成一个,再统一分析,比来回切换文件舒服得多。

7.3 一些让我明显提速的界面技巧

Wireshark界面里藏着不少节省时间的小技巧。

第一,包列表的颜色规则是可以自定义的。比如我要重点看某个客户端的TCP重置包,就新建一条规则:满足tcp.flags.reset == 1且ip.src_host == 192.0.2.10时,底色设为亮黄。这样目光一扫就能定位风险点。

第二,在包列表上点击任意一列的表头,可以直接按该列排序。双击包列表里“Info”列的摘要,会跳转到对应协议的更多细节,比逐层展开树快多了。

第三,Ctrl+Alt+Shift+T可以直接追踪当前包的TCP流,不需要多点两次右键。

第四,经常使用“分析 → 专家信息”。Wireshark已经把常见的错误、警告、备注汇总到一起,比如“前一个段的丢失”“重传”“虚假重传”等,用专家信息面板过滤,很快就能把混杂流量里的异常挑出来。

8. 这些年在抓包这件事上,我最终留下的习惯

讲了一堆功能和技巧,最后分享几条我自己的“工作纪律”。抓包这件事,很多时候不是技术不够,而是思路和习惯没建立起来。

我现在处理任何网络疑难杂症,第一件事永远是“确保抓包文件里有足够长的时间窗口和足够少的目标流量”。宁可抓得更久一点,也绝不在故障还没复现时就急着收工。第二件事是“先看统计视图,再看包列表”——统计对话、IO图表、TCP流图这三个入口能帮我把问题范围迅速缩小到某条连接、某个时间段、某个标志位。第三件事是“所有结论都必须回到业务逻辑上去验证”。Wireshark给出的所有字段都只是线索,不是定论。举个最简单的例子:你看到一个TCP包带有RST标志,它可能是防火墙主动发的,也可能是对端应用进程崩溃时内核清理连接发的。同样一个现象,背后的原因可能南辕北辙。

个人最受益的一个习惯是:每次解决完一个案例,我都会把该次抓包文件里最关键的20个包导出成一个“案例包”,并附上一段注释,说明当时的现象和结论。三个月后回头看,这些案例包简直是最好的教材——比任何官方文档都更贴近真实故障的纹理。

如果你准备在真实环境里实践,我建议先从一个简单的实验开始:在你自己的电脑上打开Wireshark,访问几个网站,然后停止抓包,过滤出你访问那个网站的TCP流,从头到尾把握手、传输、挥手走一遍,再对照着看看TLS握手有哪几个往返。这个过程不用花太多时间,但对建立“数据包思维”特别有帮助。等你看腻了自己电脑的流量,再去服务器上挑一个真实问题练手,你会发现,这个工具能带给你的东西,远比“抓包”两个字表面写的要多得多。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进