问题背景

TCP 传输,本地同 Host,跨 VM,以多线程形式/多进程形式使用 TCP Socket 传输二进制文件。

测试方式:netcat 测试,文件大小 10M,据客户反馈 5M 大小之后即存在此问题。

测试环境:

  • Windows 10 Host + 2 Windows VM guests on VMWare
  • Arch Linux Host + Docker Container * 2 (both Debian) as guests

问题描述

两边传输过程中使用 Wireshark 抓包,出现如下问题:

Wireshark 抓包设置

此处发现默认配置的 Buffer 为 2M,

传输过程中出现:

TCP ACKed unseen segments

发现存在丢包。

尝试解决问题

猜测

  • 虚拟化平台问题?

答:不会,VMWare 和 Docker 同时复现该问题,且收发两端的文件经过 SHA256 Hash 后得到的 Hash 值是一致的,说明问题在抓包环节。

  • 内核 TCP WMEM/RMEM 问题或者 拥堵算法 问题?

答:猜测无据。经学弟提醒,TCP 为可靠传输,存在重传和纠错机制。而 Docker 是一个半虚拟化的平台,使用的网卡和内核参数均与宿主机一致。我的宿主机 TCP 传输参数 net.ipv4.tcp_wmem = 4096 1048576 16777216 等均为优化过的较大值,拥堵算法为 BBR,被大规模用于数据中心,因此不应该存在此类问题,Pass. 而后续使用 TCPDUMP 抓包,显示 0 packets dropped by kernel 也说明这个猜想是错误的。

  • 那就是抓包软件问题?

答:你猜对了。根据参考文献,的确存在这个问题。Wireshark 默认 Buffer 为 2M,对于同 Host 虚拟机传输较大文件而言,这个 Buffer 是属于过小的。而且 Wireshark 默认实时滚动显示抓到的数据包,在高速大流量情况下程序运行负担较重(具体内部原因未知,欢迎各位评论区留言,其他用户有反馈,详见参考文献 1)。Wireshark 也有类似说明,并不是为了高速大流量抓包而制作。

解决办法

(1) 修改 Wireshark 默认的 Buffer Size

右侧修改 Buffer , 从默认的 2 改为 8。

需要注意的是,Wireshark 只能支持最大 8M 的 buffer size,笔者尝试修改为 10M,程序直接 SIGSEGV 退出。猜测可能跟 libpcap 有关。

(2) 修改 Wireshark 的抓包显示为不实时显示

修改抓包显示

题外:抓包完尝试还原导出 Binary 时也请耐心等待,若未等待统计数据包完成而直接保存还原,可能导致还原出的文件不完整。

Patrick Young

(3) 改用 tcpdump 抓包

实际就是最古老的抓包器,但是依然好使能用,原生工作在 Linux ,Windows 有交叉编译版本。

-w 指定写入抓包到文件,-i 指定网卡,-s 指定抓取的单包的最大体积。经测试 tcpdump 抓包不存在此问题,稳定还原。

老版本的 Tcpdump 会将单包截断为 68 或 96 字节。参见参考文献 2 。

Patrick Young

参考文献

  1. https://osqa-ask.wireshark.org/questions/25391/are-there-conditions-that-can-cause-wireshark-to-drop-packets
  2. https://www.wireshark.org/docs/wsug_html_chunked/AppToolstcpdump.html

Last modified: 2020-04-23

Author