ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

抓包实测TCP吞吐量:千兆带宽跑不满的真相

抓包实测TCP吞吐量:千兆带宽跑不满的真相

1. 为什么“1G带宽跑不满”是个值得动手实测的问题

很多人第一次看到监控面板上那条吞吐曲线时都会犯嘀咕:明明买的是千兆链路,交换机端口协商显示1000Mbps,可实际文件传输速度死活卡在六七百兆,甚至更低。于是开始怀疑运营商、怀疑网线、怀疑设备,唯独没怀疑过“吞吐量”这个指标本身到底是怎么算出来的。我当年也是这样,抱着“带宽就是水管粗细”的朴素认知,结果被现实按在地上摩擦了好几轮。

这篇内容想干的事情很直接:抛开那些背了又忘的公式,直接打开抓包工具,用真实数据把TCP吞吐量算一遍,看看千兆链路里那消失的三四百兆到底去哪了。核心关键词就三个——抓包实测、TCP吞吐量、带宽跑不满。它解决的不是“怎么配网络”的问题,而是“怎么用数据说话”的问题:当你面对一条跑不满的链路时,能自己抓包、自己算、自己定位瓶颈,而不是靠猜。

适合谁来读?网络运维、后端开发、测试工程师,以及任何被“带宽够但速度不够”困扰过的技术人。不需要你精通TCP协议栈,但得愿意动手敲几条命令、看一眼抓包结果。下面所有实操都基于通用工具和虚拟环境,你可以直接照着复现。

2. 先把“吞吐量”这件事想明白:设计思路与方案选型

2.1 为什么不用公式,而用抓包

教科书上的吞吐量公式大概是这样的:吞吐量 ≈ 窗口大小 / RTT。看着简洁,但它是个理想模型,默认链路无丢包、无拥塞、接收窗口恒定。真实网络里这些假设一个都不成立。你按公式算出来理论值能跑满千兆,实际却差一大截,问题就出在“公式没告诉你的事”上。

抓包的价值在于,它记录的是链路上真实发生的每一个字节。你可以从抓包文件里直接读出:实际传输了多少数据、花了多长时间、有没有重传、窗口有没有缩小、有没有零窗口通告。这些信息拼起来,才是吞吐量跑不满的真实原因。公式给你一个上限,抓包告诉你为什么没到上限。

我选择用抓包而不是只看监控面板,还有一个很实际的原因:监控面板的采样粒度通常是秒级甚至分钟级,短时间的突发、重传、窗口抖动全被平均掉了。而抓包是微秒级的,任何一次异常都能被揪出来。这就像看病,体检报告是平均值,抓包是切片病理。

2.2 实验环境怎么搭才可控

要实测,环境必须可控。我用的是一套完全虚拟化的方案,避免真实硬件带来的干扰变量:

  • 两台虚拟机,分别作为发送端和接收端,操作系统用常见的Linux发行版。
  • 中间用虚拟网络连接,链路速率人为限制在1Gbps,模拟千兆环境。
  • 抓包点在接收端网卡上,用抓包工具的命令行版本,避免图形界面带来的额外开销。
  • 传输工具用文件传输和自带的流量生成工具两种,交叉验证。

为什么要在接收端抓包而不是发送端?因为接收端看到的是“实际到达”的数据,重传、乱序、丢包的影响都体现在这里,更接近用户感知到的吞吐量。发送端抓包看到的是“发出去”的,容易高估。

提示:虚拟环境里限制链路速率,建议用流量控制工具做整形,而不是靠虚拟机软件自带的限速,后者粒度粗、抖动大,会让实测数据失真。

2.3 关键指标先定义清楚,别算错

动手之前得统一口径,否则算出来的数字没法对比。我关注四个量:

指标含义抓包里的来源
传输字节数应用层有效载荷总量TCP段长度累加,剔除头部
传输耗时从第一个数据段到最后一个ACK的时间首末包时间戳之差
重传率重传段数占总段数比例抓包工具的重传标记
平均窗口接收窗口的平均值TCP头部窗口字段

吞吐量 = 传输字节数 × 8 / 传输耗时,单位换算成Mbps。注意这里用的是应用层有效载荷,不是链路层帧长。很多人算出来偏大,就是把以太网头、IP头、TCP头都算进去了,那些是开销不是有效数据。这个细节后面还会展开。

3. 抓包实操:从零跑出一组可信数据

3.1 抓包前的准备工作

第一步是把抓包工具装好。命令行版本在主流Linux发行版里基本都能直接装,装完确认版本,老版本对新协议字段的解析可能不全。接着确认网卡名称,用系统自带的网络信息命令看一眼,别抓错网卡——虚拟环境里经常有好几块网卡,抓错了等于白忙。

第二步是设置抓包过滤器。这一步极其关键,不加过滤会把整条链路的杂包全抓进来,文件巨大还干扰分析。我的过滤条件是只抓目标端口上的TCP流量:

tcpdump -i eth0 -s 0 -w tcp_test.pcap 'tcp port 5201'

参数逐个说清楚:-i eth0指定网卡,-s 0表示抓完整包不截断(默认只抓前几十字节,头部信息会丢),-w写入文件,最后的过滤表达式限定端口。端口5201是流量生成工具的默认端口,换成你自己的传输端口即可。

注意:-s 0一定要加。我踩过的坑就是忘了加,结果抓下来的包每个只有几十字节,TCP选项字段全被截断,窗口缩放因子读不出来,后面算窗口全是错的。

3.2 发起传输并同步记录时间

抓包启动后,立刻在发送端发起传输。我建议同时记录一个“应用层开始时间”和“应用层结束时间”,作为交叉验证。抓包算出来的是网络层视角的耗时,应用层视角会多出一些系统调用和磁盘IO的时间,两者对比能判断瓶颈是不是在主机侧。

传输持续30秒左右比较合适。太短了抓不到稳态,太长了文件大、分析慢。30秒在千兆链路上能传好几个GB,足够看出趋势。传输过程中不要动其他网络配置,保持环境干净。

传输结束后先停抓包,再停传输工具,顺序别反。反了的话抓包文件里会缺最后一段,算出来的耗时偏短、吞吐量偏大。这个细节很小,但直接影响结果可信度。

3.3 用抓包工具读出原始数字

抓包文件拿到手,先用抓包工具的统计功能看个大概。命令行版本可以用读取模式配合统计参数,图形界面版本直接看“统计”菜单里的“对话”视图。重点看三个数:总字节数、总包数、起止时间。

我实测的一组原始数据是这样的:30秒传输,抓到的TCP段总数约26万个,累计有效载荷约2.4GB,首包到末包的时间跨度29.6秒。粗算一下:2.4GB × 8 / 29.6s ≈ 649Mbps。千兆链路,只跑到649Mbps,缺口超过三分之一。这就是标题里说的“跑不满”,接下来就是找原因。

这里要强调一个易错点:抓包工具统计的“字节数”默认可能包含链路层头部。一定要在统计设置里选择“只统计TCP有效载荷”,或者手动用过滤条件剔除。我见过有人直接拿工具默认数字算,结果算出来比链路速率还高,闹了笑话。

3.4 把数字代进公式,但要知道公式的边界

用抓包数据算吞吐量,公式本身很简单:

吞吐量(Mbps) = 有效载荷字节数 × 8 / 传输耗时(秒) / 1,000,000

代入上面的数据:2.4 × 1024 × 1024 × 1024 × 8 / 29.6 / 1,000,000 ≈ 649Mbps。和粗算一致。

但这个公式只回答“实际跑了多少”,不回答“为什么没跑满”。要回答后者,得往下钻:看重传、看窗口、看往返时延。公式是入口,不是终点。很多人算完吞吐量就停了,觉得“哦,就是跑不满”,这等于只做了体检没看报告。

4. 逐层拆解:那消失的350Mbps到底去哪了

4.1 第一层:协议开销,先扣掉固定成本

千兆指的是链路层速率,也就是每秒能传多少以太网帧。但用户数据要经过层层封装:以太网头14字节、IP头20字节、TCP头20字节(不含选项),加起来54字节。一个满载的以太网帧有效载荷是1500字节,所以有效数据占比是1500 / (1500 + 54) ≈ 96.5%。

这一层就吃掉了3.5%左右,千兆变成约965Mbps。这是固定成本,躲不掉。如果TCP头部带了时间戳等选项,头部还会更长,占比进一步下降。抓包里可以直接看到每个段的头部长度,统计一下平均值就知道实际开销。

提示:别小看这3.5%。在万兆链路上,协议开销的绝对值更大,做容量规划时必须把它算进去,否则永远对不上账。

4.2 第二层:往返时延与窗口,吞吐量的真正天花板

扣掉协议开销还有965Mbps,实测只有649Mbps,缺口还有300多。这时候要看窗口和往返时延。抓包里读接收窗口字段,取稳态阶段的平均值,我这次读到约64KB(受窗口缩放因子影响,实际值要乘上缩放倍数,抓包工具一般会自动算好显示)。

往返时延从抓包里量:找一个数据段和它对应的ACK,时间戳相减。我这次环境里约2ms。代入带宽时延积:64KB × 8 / 0.002s = 256Mbps。等等,这比实测的649Mbps还低,说明窗口不是恒定64KB,而是动态变化的,峰值更高。抓包里看窗口随时间的变化曲线,峰值能到200KB以上,对应约800Mbps的理论上限。

这就解释了为什么跑不到965Mbps:窗口不够大,填不满管道。带宽时延积决定了“管道里能装多少数据”,窗口小于这个值,发送端就得停下来等ACK,链路出现空闲。这是吞吐量跑不满最常见的原因,没有之一。

4.3 第三层:重传与拥塞控制,看不见的刹车

再看重传。抓包工具能标出重传段,我这次统计重传率约0.8%。看着不高,但TCP拥塞控制对丢包极其敏感,一次丢包就可能把拥塞窗口砍半,然后慢慢爬升。0.8%的重传率,足以让平均窗口比峰值低一大截。

抓包里还能看到拥塞窗口的变化(部分工具支持解析),呈现典型的锯齿状:爬升、丢包、砍半、再爬升。每一次砍半,链路就空闲一段时间。这些空闲时间累加起来,就是那消失的几百兆。

重传的原因这次是虚拟环境里的队列溢出,不是链路误码。真实环境里还要考虑双工不匹配、网线质量、中间设备缓冲不足等。抓包能告诉你“有重传”,但“为什么重传”还得结合环境判断。这就是为什么我一直强调环境要可控——变量太多,根本没法归因。

4.4 第四层:主机侧瓶颈,别忽略CPU和内存

还有一类原因藏在主机里,抓包不一定直接体现,但能从时间戳的规律性看出来。如果数据段的发送间隔呈现明显的周期性抖动,或者ACK的返回时间忽快忽慢,可能是发送端CPU跑满了、内存拷贝成了瓶颈、或者中断处理跟不上。

我这次特意在传输时看了发送端的CPU使用率,单核跑到接近饱和。流量生成工具本身要消耗CPU做加密和拷贝,千兆线速下这个开销不小。换成普通文件传输,CPU压力小一些,吞吐量反而略高。这说明主机侧的处理能力也是吞吐量的一部分,链路再宽,主机喂不满也白搭。

5. 常见问题与排查速查表

实操过程中遇到的问题不少,挑几个典型的整理成表,方便对照排查。

现象可能原因排查手段处理方向
抓包文件巨大,分析卡死未加过滤器,抓了全链路流量检查抓包命令的过滤表达式加端口或主机过滤,只抓目标流量
算出的吞吐量比链路速率还高统计时把协议头部算进了有效载荷核对统计口径只统计TCP有效载荷
窗口字段读出来是0或异常小抓包时截断了包,选项字段丢失检查是否加了-s 0重新抓完整包
重传率很高但链路质量看着正常中间设备队列溢出或双工不匹配看重传的时间分布是否集中检查交换机端口配置和缓冲
吞吐量忽高忽低无规律主机侧CPU或IO瓶颈传输时监控主机资源换更轻量的传输工具或加核
首末包时间对不上应用层耗时抓包启停顺序反了核对操作顺序先启抓包,后停传输

除了表里的,再补两个独家避坑点。第一,虚拟环境的时钟可能不同步,抓包时间戳和主机时间有偏差,算耗时一定要用抓包文件内部的时间戳,别混用。第二,抓包工具本身在高流量下会丢包,如果发现抓到的段数明显少于发送端统计,说明抓包点扛不住了,得换更靠前的抓包位置或者降低速率。

6. 把结论落到可复现的操作上

整套流程走下来,其实就四步:搭可控环境、抓完整包、算有效吞吐、逐层归因。每一步都有坑,但坑都是可预见的。我个人在实际操作中的体会是,抓包最大的价值不是算出那个数字,而是让你对“带宽”这个词祛魅。千兆不是每秒传1000兆用户数据,它是一个链路层速率,上面叠着协议开销、窗口限制、拥塞控制、主机处理能力四道关卡,每道关卡都要交过路费。

如果你也想复现,建议从最小的环境开始:两台机器、一条限速链路、一个固定端口的传输。先把649Mbps这个数跑出来,再逐项调整窗口大小、往返时延、重传率,看吞吐量怎么变。调参的过程比看结论有意思得多,也更能建立直觉。等你能凭抓包曲线一眼看出瓶颈在窗口还是在重传,这套方法就算真正学到手了。后续还可以往深了做,比如对比不同拥塞控制算法在同一链路下的表现,或者把抓包数据导出来做时序分析,那又是另一个话题了。

RELATED READING

延伸阅读

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