ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

用Wireshark统计发送数据包长度:从抓包到定位应用层传输问题

用Wireshark统计发送数据包长度:从抓包到定位应用层传输问题 聊个很多人都会忽略的细节。抓包不难难的是从抓回来的几千个数据包里看出问题。有一次朋友内网传文件奇慢网卡明明是千兆实际吞吐只有几十兆我让他把抓包文件发过来随手打开 Statistics 里的 Packet Lengths一眼就看出了问题——他那个程序发出的包一半以上都集中在几十字节的区间。这篇文章就把统计发送的数据包长度这件事讲透从怎么抓、怎么过滤、怎么统计到怎么从一张包长分布图里反推出应用层到底干了什么。适合刚会用 Wireshark 抓包、但面对一堆数据不知道从哪下手的同学也适合想用数据说话的网络运维。1. 包长统计能看出什么三个最典型的实战场景1.1 小包多、吞吐低先看包长分布再去找原因先说第一个场景。以太网帧本身有开销帧间隙 12 字节前导码 8 字节加上 Ethernet 头 14 字节一个 64 字节的最小帧实际在链路上要占 84 字节。如果你发的全是小包有效载荷占比就非常低。举个例子64 字节帧里 IP 头占 20 字节TCP 头占 20 字节真正给应用的数据只有 24 字节左右有效载荷率不到三成。千兆网卡每秒能处理的包数是有上限的以入门级网卡百万包每秒来算如果全发 64 字节小包理论吞吐也就 500Mbps 左右和你千兆的口子相比直接腰斩。所以当你发现吞吐上不去最快的一步就是先看看包长分布里小包占比是不是异常高。这就是包长统计的第一个价值快速锁定是不是小包拖垮了吞吐。你不用去算包每秒也不用查网卡参数只要 Packet Lengths 窗口里 4079 字节这个区间占了绝对大头心里就要有数了。1.2 验证应用有没有吃满 MTU第二个场景是反向验证。比如你调一个上传模块怀疑它没有善用 MTU把大文件拆成了很多小块去发送。以太网标准 MTU 是 1500加上 14 字节以太头数据包在链路上最大通常是 1514 字节带 VLAN 标签的话还要再加 4 字节变成 1518。如果 TCP 分段正常一个持续传输的 TCP 流里应该能看到大量 1514 字节左右的满载帧因为应用写多少数据TCP 层都会切成 MSS 大小一般 1460 字节的段发出去。反过来假如你统计发送的包发现 1000 字节以上的帧几乎没有全是几百字节的那问题基本就出在应用层写缓冲区太小、每次发送的字节数太少或者 Nagle 算法和延迟 ACK 叠加导致吞吐劣化。这里顺带说一句很多人以为我发送了多大抓包就应该看到多大的帧这个想法是错的。TCP 是流协议它不保证你的应用写入边界和网络包边界一一对应中间还有分段和粘包。所以用包长统计去验证应用程序有没有高效利用网络本质上是看帧长度的分布形态而不是看单次发送的大小。1.3 给正常流量建基线异常一眼可见第三个场景也是我比较喜欢用的把包长分布当作流量的指纹。一个业务系统在稳定运行的时候它的包长分布是相对固定的。比如一个心跳类服务绝大多数包在 60100 字节一个视频推流服务大包占绝大多数一个 Web 服务则是典型的双峰分布既有几十字节的 ACK 和请求也有 1400 字节左右的大响应。一旦某天分布形态变了比如大包比例骤降、或者突然冒出大量超大帧就说明业务行为有变化或者是网络中间设备出了状况。你不需要读懂每一个包只需要定期导出一份包长统计做对比异常就能在很早期暴露。这不是什么高深的东西但实操中真的很管用比盯着一堆告警面板直观得多。2. 抓包前先把发送两个字定义清楚2.1 抓包位置决定了发送的方向标题里有个词容易被忽略——发送。在开始统计之前你得先想明白你抓到的数据包哪些算发送这取决于你抓包的位置。如果直接在业务服务器上抓包Wireshark 会同时看到两个方向的流量进来的和出去的。对服务器来说出去的包也就是它发送的包通常用ip.src 服务器IP来过滤对客户端来说则是ip.src 客户端IP。注意这里说的是 IP 层方向不是数据链路层。但如果你是在交换机镜像口抓包情况就复杂一点。镜像口上看到的流量是双向的你得根据设备的 MAC 或者 IP 来判定方向。假如两台服务器 A 和 B 通信你在核心交换机上做了端口镜像这个口连的是 A那看到的帧里源 MAC 为 A 的就是 A 发送的源 MAC 为 B 的就是 A 接收的。所以发送不是一个绝对概念它跟着抓包点走抓包前必须把这个口子捋清楚。2.2 用 BPF 捕获过滤器减小干扰想清楚方向之后建议在抓包阶段就用捕获过滤器把流量圈小一点别一股脑全抓。BPF 语法里src host 192.168.1.100表示只抓源地址是这个 IP 的包配合tcp or udp可以限定协议。比如我要统计某台客户端发给服务器的包可以这样写src host 192.168.1.100 and tcp这样抓下来的文件会干净很多Wireshark 处理起来也快文件还不容易大到几百兆。不过要注意捕获过滤器和显示过滤器是两套语法捕获过滤器用 BPF显示过滤器用的是 Wireshark 自己的语法比如ip.src 192.168.1.100。很多人把这两者搞混在捕获过滤器里输入ip.src1.2.3.4结果一个包都抓不到这种低级错误我见过不止一次写的时候留意一下输入框的背景色就行BPF 语法输入错误会直接提示。2.3 分清 frame.len、ip.len、tcp.len、data.len正式开始统计之前有一个最基础的概念必须理清所谓数据包长度到底指的是哪个长度Wireshark 里和长度相关的字段有好几个统计的时候选错了结论就全歪了。字段含义典型用途frame.len整个帧在链路上的长度含以太网头包长分布统计首选frame.captured_len实际捕获到的字节数可能被 snaplen 截断判断抓包是否截断ip.lenIP 包总长度不含以太网头排除以太头干扰tcp.lenTCP 段的载荷长度不含 TCP 头和 IP 头看应用数据分段data.len应用层数据长度配合上层协议分析打个比方frame.len 相当于一个快递包裹的整体尺寸ip.len 是去掉外包装之后箱子的大小tcp.len 才是里面商品本身的体积。你做发送的数据包长度统计如果没有特别说明我建议直接用 frame.len因为它才是最真实的线上尺寸也和网卡、交换机、抓包工具的统计口径一致。后面我讲的命令、操作默认都是 frame.len。3. Wireshark 图形界面统计发送包长一步步操作3.1 Packet Lengths 统计窗口怎么读假设你已经用src host过滤器抓好了包现在打开 Wireshark点击菜单 Statistics - Packet Lengths会弹出一个分区间的统计表。默认分组是 40-79、80-159、160-319、320-639、640-1279、1280-2559 这些区间每一行显示这个区间内的包数量、占比、累计数量、累计占比。窗口最上面还有一行总的包数量和平均包长。读这个表有个诀窍先看最大那个区间的占比。比如一个 TCP 下载流1280-2559 区间应该占大头因为满载帧 1514 字节正好落在这个区间如果这个区间几乎没有说明链路上根本没有大包在跑。再看 40-79 区间这里是 TCP ACK 和各类小包控制包的聚集地纯下载场景下它会有一定比例但不会高得离谱。两个区间一对比传输效率大概什么样心里就有数了。Packet Lengths 窗口里也可以直接输入显示过滤器比如我只想统计某个 IP 发送的包就在窗口左下方的过滤框里输入ip.src 192.168.1.100再点 Apply统计表会实时变成过滤后的结果。这个功能很多人没注意到其实是挺好用的省得在主界面反复切换过滤器。3.2 用显示过滤器精确圈定自己发的包如果是抓的完整双向流量想单独统计发送方向就在主界面的显示过滤器里设置ip.src 192.168.1.100然后等过滤器框变绿再去看统计。这里有个小细节如果你只关心 TCP 而且不在乎 TCP 重传、乱序等异常包可以在后面加上 !tcp.analysis.flags来过滤掉所有 TCP 异常标记包这个我们在第 5 节会详细讲统计基线一定要用干净的流量。还有一类场景是抓 HTTP 或 HTTPS 流量。发出去的请求包都很小回来的响应包很大如果你只统计发送就会看到包长分布正好是小包为主这是完全正常的。所以每次做统计的时候一定要记住你的业务流特征别拿一个结果硬套所有场景。另外Wireshark 的会话统计Statistics - Conversations和端点统计Statistics - Endpoints里也有按方向拆分的字节数和包数。比如在 Conversations 窗口里选中一个 TCP 会话可以看到 A-B 和 B-A 两个方向的包数和字节数点击 Bytes 列还能排序找到最肥的会话。这虽然不是严格意义上的包长分布但配合 Packet Lengths 可以快速定位到具体是哪个连接在大量发包是排查谁在偷偷占带宽的好工具。3.3 IO Graph 动态观察包长变化趋势分布统计看的是整体形态有时候你还想知道包长随时间怎么变这时候就要用 IO Graph。菜单 Statistics - IO Graph默认是一张所有流量的速率折线图单位是包每秒。重点是左侧的 Filter 栏你可以输入多个条件同时画多条线。比如我想看大于 1400 字节的发送包和小于 100 字节的发送包分别是什么趋势可以配置两条规则第一条 Filter 填ip.src 192.168.1.100 frame.len 1400第二条 Filter 填ip.src 192.168.1.100 frame.len 100把 Y Axis 改成 Bytes单位选 Bytes/tick这样两条线的纵坐标就是字节数图形出来后非常直观如果大包那条线在某些时间段突然断了而小包线还一直撑着说明那个时间段里应用写数据的节奏出了问题。IO Graph 的 tick 间隔可以调一般 1 秒就能看得很清楚了时间跨度大就改成 5 秒或者 10 秒。这里提醒一下IO Graph 默认统计的是符合过滤器的包数量不是字节数看吞吐趋势一定要在 Y Axis 里改成 Bytes。4. 要精确数值就上 tshark命令行统计平均包长与分位数GUI 的 Packet Lengths 已经很直观了但如果你要写报告、要精确的平均包长、要 P50/P95 分位数或者要批量处理几十个抓包文件还是得上 tshark。tshark 和 Wireshark 用的是同一套解析引擎你不用担心统计口径不一致它只是把图形界面变成了命令行。4.1 一条命令算出平均长度、最大最小长度最简单的用法先过滤出想要统计的包然后只输出 frame.len 字段再交给 awk 做计算。假设抓包文件叫 send.pcapng统计 192.168.1.100 发送的 TCP 包长度tshark -r send.pcapng -Y ip.src 192.168.1.100 tcp -T fields -e frame.len | awk {sum $1; if ($1 max) max $1; if (min 0 || $1 min) min $1; count} END {printf count%d, avg%.1f, min%d, max%d\n, count, sum/count, min, max}输出类似于count85432, avg1178.3, min54, max1514这样就拿到了最核心的三个数。如果想看分位数可以用 sort 排序后再算。比如算 P95也就是从小到大排在第 95% 位置的那个包长tshark -r send.pcapng -Y ip.src 192.168.1.100 tcp -T fields -e frame.len | sort -n | awk {arr[NR] $1} END {print P95 , arr[int(NR * 0.95)]}这些都是 Linux 和 macOS 自带的工具Windows 上没有 awk 的话可以用 Git Bash 或者 WSL 跑。嫌麻烦也可以把 tshark 输出重定向到文件再用 Excel 打开排序方法多的是重点是拿到数字之后要能解释得通。4.2 按目的地址和协议做分组统计光有整体平均还不够有时你要知道这台机器发给不同服务器的包长有没有差异。比如它给数据库服务器发的是大包给监控服务器发的是小包混在一起统计就看不清了。tshark 有官方的统计模块用 -z 参数就可以直接出分组结果tshark -r send.pcapng -q -z conv,ip这条命令输出所有 IP 会话的双向统计包括包数、字节数、平均包长。如果你只想看单向可以先过滤再统计tshark -r send.pcapng -Y ip.src 192.168.1.100 -q -z conv,ip还有一个非常实用的-z io,stattshark -r send.pcapng -q -z io,stat,10这个按 10 秒一个区间输出每个时间窗口的帧数、字节数、平均包长。字段分别是 Frames、Bytes、Avg frame bytes一眼就能看出哪个时段吞吐异常。想加过滤条件也可以比如只看大包tshark -r send.pcapng -q -z io,stat,10,ip.src 192.168.1.100 frame.len 1400这样每个窗口里的大包数量、字节数都有了和 GUI 的 IO Graph 效果一样但更容易写进脚本批量处理。4.3 按时间窗口切片看包长波动配合过滤器你还可以针对特定协议做切片。比如只看 UDP 语音流量的包长分布tshark -r voip.pcapng -Y udp ip.src 192.168.1.100 -T fields -e frame.len | sort -n | uniq -c | sort -rn | head -20这会把最常出现的包长前十名列出来G.711 语音流通常是 200 多字节的帧G.729 则在 70 字节上下。看到这种高度集中在一个长度的分布基本就能判断是固定时长的语音包。如果你发现一堆长度乱七八糟的 UDP 包那可能是应用在逐字节地往外吐数据性能问题基本跑不掉。这里给个忠告tshark 的输出字段顺序、引号嵌套在不同平台上有细微差别Windows PowerShell 里双引号容易出问题建议要么用单引号括过滤器要么把命令写进脚本再执行。别在命令行里反复试容易把自己绕晕。5. 包长统计容易踩的三个坑及完整排查过程5.1 为什么文件里最大只有 520 字节应用却说发了 2090 字节这是一个非常经典的问题网上也经常有人问。你写了个程序调一次发送接口发了 2090 字节抓包一看最大的包才 520 字节第一反应就是怀疑抓包工具截断了。先别急着怪 Wireshark我带你完整走一遍排查链路。第一步先看包的 frame.len 和 frame.captured_len 是否一致。在 Wireshark 里选中那个 520 字节的包展开 Frame 层如果frame.captured_len frame.len说明抓包没有截断snaplen 没问题如果 captured_len 明显小于 len那才是真正的截断去 Capture Options 里把限制每个包的长度选项改成 65535 再抓一次。第二步确认是不是 TCP 分段。如果第一步确认没有截断那大概率是这个 2090 字节被 TCP 拆成了多段。以太网 MTU 1500减去 IP 头 20 字节和 TCP 头 20 字节MSS 就是 1460。2090 字节的数据会被拆成 1460 加 630 两段链路层帧长分别约为 1514 和 684。你看到最大只有 520 字节很可能是过滤条件把 1514 的大帧筛掉了或者这个文件里大多数是别的流量。用前面第 4 节的 max 计算一看便知tshark -r send.pcapng -Y ip.src 你的IP -T fields -e frame.len | sort -n | tail第三步看看是不是 MTU 异常小。如果整个会话里最大帧长确实只有 520 多字节且所有大包都被切成了这个尺寸那就要怀疑链路 MTU 是不是被调小了比如是不是走了某种隧道封装、拨号链路或者中间设备做了 MSS 钳制。这种情况下包长统计表会呈现一个很奇怪的现象所有包的帧长都被削到同一个很小的上限附近没有正常的 1514 帧。确认方法很简单看看有没有对应的小包 ICMP 差错报文或者直接在链路两端跑一次大包 ping 测试。第四步关于怎么显示 2090 字节。如果你关心的是应用层整体发送了多少数据不要试图在单包里找 2090而应该用 Follow TCP Stream 看重组后的完整流或者用 Statistics - Flow Graph 确认同一序号区间的分包情况。TCP 是流不是包统计发送的数据包长度统计的是 TCP 分段后的线上帧不是应用写入大小这两件事必须分开。5.2 TSO/GRO 卸载让包长数据失真第二个坑和网卡卸载功能有关。Linux 和 Windows 的网卡驱动普遍开启了 TSOTCP 分段卸载和 GRO接收侧合并。TSO 的意思是应用一次发送大块数据驱动直接把这个大块交给网卡网卡硬件自己去做 TCP 分段。在抓包软件看来有时会看到超过 MTU 的超大帧比如 6000 多字节的超级帧这不是网络真的在传巨型帧而是抓包点拿到了网卡驱动里尚未分段的数据。反过来GRO 在接收方向把多个小段合并成一个较大的帧交给上层抓包也可能看到合并后的长度。于是你的包长统计会出现一些看起来不真实的数值要么冒出 4000、6000 字节的包要么所有包都偏大。这不是 Wireshark 的问题是你的抓包点在网卡驱动和协议栈之间看到的是卸载前后的中间状态。解决办法有三个。最简单的是统计时心里有数知道哪些是超大帧别把它们当成线上真实帧长第二个办法是在被测机器上关掉卸载功能Linux 下用ethtool -K eth0 tso off gro off gso offWindows 下在网卡高级属性里关闭大量发送卸载第三个办法是换抓包位置比如用交换机镜像口那里看到的是完全真实的线上帧。我一般建议先不关卸载抓完看到异常再决定因为这本身也是排查线索——如果某台机器突然出现大量超大帧先检查它的卸载设置是不是被动过。5.3 重传包、乱序包混入统计导致结论跑偏第三个坑比较隐蔽。TCP 丢包重传的时候同一个序号的数据会在抓包里出现多次。如果你只是按ip.src过滤统计发送包长重传的包会被重复计算包数和字节数都会虚高。更麻烦的是超时重传的包往往是应用已经写出的同一批数据重复计数之后你统计出来的发送数据包长度和真实的新数据发送量会有偏差。怎么避免在显示过滤器里加一个排除条件把 TCP 分析标记过滤掉ip.src 192.168.1.100 !tcp.analysis.flagstcp.analysis.flags会把乱序、重传、快速重传、重复 ACK、零窗口等一堆异常标记全部涵盖是排查阶段保持统计干净的最好工具。如果你想保留重传做专门分析就把!tcp.analysis.flags去掉但统计基线数据建议始终排除。另外还有一个容易被忽略的ACK 包在双向各自统计。一个完整的 TCP 会话A 发给 B 的是数据大包B 发给 A 的是 ACK 小包。如果统计脚本只按 IP 方向抓没问题如果你误用了双向统计会把 ACK 的 54 字节小包全部算进发送结果平均包长被拉得很低看起来像应用没发大包其实是统计口径错了。这个问题在 GUI 的 Conversations 里最明显它默认会分两个方向列出来用的时候注意别只看合计列。6. 一个真实案例从包长分布反推应用的发送行为说了这么多理论最后讲一个我前段时间处理的案例把整个思路串一遍。有个同事负责的日志上报模块出了问题客户反馈说上报速度慢一个 20MB 的日志文件要传十几分钟。他把抓包文件发给我我第一件事就是看包长分布。用 Packet Lengths 打开结果非常异常1280-2559 区间几乎为零绝大多数包集中在 64 到 200 字节之间。这说明这个模块根本没有用满 MTU。然后我用显示过滤器ip.src 客户端IP看发送方向的包长发现平均包长只有 180 字节。按这个平均值算一个 20MB 文件要拆成差不多 12 万个包而同样数据量如果正常按 1460 字节 MSS 发送只要 1.4 万个包左右。包数量差了将近 9 倍传输效率自然上不去。再往下挖问题出在应用代码上。同事的日志模块为了实时上报把每一条日志都当成一次单独的网络写入每条日志短的几十字节长的几百字节。也就是说应用的写缓冲太小TCP 层几乎每次都要发出一个不满载的段。再加上 Nagle 算法和延迟 ACK 的相互作用有时候还会产生额外的等待进一步放大延迟。这里顺便说一句我当时怎么确认是 Nagle 导致的延迟用 IO Graph 的 1 秒粒度看发送速率能明显看到周期性一坨一坨的突发而不是平滑的流这个特征就是经典表现。让同事在 socket 上开启 TCP_NODELAY并把日志攒成 4KB 左右的缓冲批量写问题迎刃而解。这个案例想说明的是包长统计不是拿来炫技的它最大的价值是帮你把应用层怎么用网络这件事变成一张可以被讨论的图。从一张分布表到平均包长的一个数字再到 IO Graph 的趋势层层递进问题就藏不住。最后再分享一个我自己的操作习惯每次抓包完成不管有没有发现问题我都会花十秒钟跑一下 Packet Lengths顺手把平均包长记在笔记里。时间长了你对自己系统的包长基线会敏感得可怕某天数字不对劲不用等用户报障你就已经知道有人改了代码。这个习惯比任何高级技巧都值钱。
返回列表