问题背景
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 抓包,出现如下问题:
此处发现默认配置的 Buffer 为 2M,
传输过程中出现:
发现存在丢包。
尝试解决问题
猜测
- 虚拟化平台问题?
答:不会,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
需要注意的是,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