
1. 网络故障模拟到底在模拟什么第一次接触“网络故障模拟”这个说法的人脑子里冒出来的画面往往是机房着火、交换机冒烟、网线被老鼠咬断。但真正做过抓包分析的人都知道日常工作中遇到的绝大多数“网络故障”其实根本不是硬件坏了而是协议行为不符合预期——TCP 该重传的时候没重传UDP 该丢包的时候丢得莫名其妙握手该三次的时候偏偏只走了两次。这些东西你在应用层是看不出来的日志里只会写一句“连接超时”然后就没有然后了。所以网络故障模拟的核心不是去制造物理损坏而是在可控环境下复现那些“看起来像故障”的协议行为再用抓包工具把整个过程录下来逐帧分析。TCP 和 UDP 是绕不开的两个主角TCP 面向连接、有状态、有重传机制它的故障往往表现为握手失败、窗口冻结、RST 异常、重传风暴UDP 无连接、无状态、不保证送达它的故障更多体现在丢包、乱序、重复、MTU 分片问题上。两者的排查思路完全不同抓包时的过滤策略也不一样。这套东西适合谁我个人的判断是三类人最需要一是刚入行的运维或后端开发线上出了问题只会重启服务想往深了走必须懂抓包二是做物联网、音视频、游戏这类对 UDP 依赖很重的开发者UDP 的问题不抓包基本靠猜三是准备面试网络相关岗位的人TCP 三次握手四次挥手背得再熟不如自己抓一次包看一遍来得扎实。接下来我会按照“先搭环境、再制造故障、然后抓包、最后逐帧分析”的顺序把整个流程拆开讲。中间会穿插大量我实际踩过的坑比如为什么你抓到的包总是缺帧、为什么 Wireshark 显示的“重传”有时候是误报、为什么 UDP 抓包过滤规则写错了会漏掉关键数据。这些细节文档里通常不会写但恰恰是决定你能不能定位到问题的关键。2. 动手之前环境搭建与工具选型2.1 抓包工具的取舍逻辑抓包工具市面上主流的就是 Wireshark、tcpdump再加上一些命令行小工具。我的建议是两个都要会但分工明确。Wireshark 是图形化分析的主力优势在于协议解码极其完整TCP 的每一个标志位、每一个选项字段都能展开看还能自动标记重传、乱序、重复 ACK。但它的劣势也很明显在服务器上跑图形界面不现实而且抓大流量的时候内存吃得厉害。tcpdump 则是服务器端的标配几乎任何 Linux 发行版都自带或者一条命令就能装它负责“抓”把包存成 pcap 文件然后拉到本地用 Wireshark 分析。这个“服务器抓、本地析”的组合是我用得最多的。至于故障模拟工具Linux 下最顺手的是tctraffic control配合netem模块可以模拟延迟、丢包、乱序、重复、损坏。Windows 下可以用 Clumsy 这类工具但功能没 tc 细。如果你只是想模拟应用层的连接异常用iptables直接 DROP 或 REJECT 某些包也能达到目的而且更简单粗暴。提示不要一上来就上复杂工具。很多“故障”用 iptables 一条规则就能造出来比如直接丢弃 SYN 包模拟服务不可达比配置 tc 快得多。2.2 实验环境的搭建步骤我习惯用两台 Linux 虚拟机加一台宿主机来做这套实验。一台当客户端一台当服务端宿主机用来跑 Wireshark 分析。网络模式用 NAT 或者仅主机模式都行关键是两台机器能互通且你能控制它们之间的流量。具体步骤两台虚拟机都装好基础系统确认ping能通。服务端装一个简单的 TCP 服务和 UDP 服务。TCP 用nc -l或者 Python 的 socket 都行UDP 同理。我一般用 Python 写个十几行的小脚本方便控制收发逻辑。客户端装tcpdump服务端也装因为有时候需要两端同时抓包对比。宿主机装 Wireshark用来打开 pcap 文件。确认tc可用tc qdisc show能输出内容就说明没问题。这里有个容易被忽略的点虚拟机的网卡类型会影响抓包结果。如果你用的是 virtio 网卡某些情况下 TCP 校验和卸载checksum offload会导致 Wireshark 显示校验和错误但那其实是网卡卸载造成的假象不是真的错误。解决办法是在 Wireshark 里关掉校验和验证或者用ethtool -K eth0 tx off rx off关掉卸载。这个坑我踩过不止一次一开始以为是数据包损坏查了半天才发现是卸载的锅。2.3 抓包过滤规则的正确写法抓包时最忌讳的就是“全抓”。全抓的后果是文件巨大、噪音极多分析的时候光过滤就浪费半小时。正确的做法是在抓的时候就过滤而不是抓完再过滤。tcpdump 的过滤表达式基于 BPF 语法几个我常用的# 只抓某台主机的 TCP 流量 tcpdump -i eth0 -w tcp.pcap host 192.168.1.100 and tcp # 只抓某个端口且排除 SSH 自身流量 tcpdump -i eth0 -w udp.pcap udp port 9999 # 抓 TCP 握手和挥手相关的包 tcpdump -i eth0 -w handshake.pcap tcp[tcpflags] (tcp-syn|tcp-fin|tcp-rst) ! 0Wireshark 的显示过滤器则是另一套语法常用的有tcp.analysis.retransmission看重传、tcp.flags.reset 1看 RST、udp.length 100按长度过滤。注意区分捕获过滤器和显示过滤器前者在抓的时候就生效后者只是隐藏不匹配的包文件里其实还在。注意抓包时加-s 0表示抓完整包不加的话默认只抓前 96 字节TCP 选项字段可能被截断分析握手参数时会缺信息。这个细节很多人不知道导致抓到的包“看起来正常”但分析不了。3. TCP 故障模拟与抓包实战3.1 三次握手失败的三种典型场景TCP 故障里最基础也最常考的就是握手失败。我把它分成三种SYN 丢失、SYN-ACK 丢失、ACK 丢失。三种情况在抓包结果上表现完全不同排查方向也不一样。场景一SYN 丢失。客户端发出 SYN 后服务端根本没收到所以不会有任何回应。抓包看到的是客户端反复重发 SYN间隔按 1s、2s、4s、8s 指数退避重试几次后放弃报“连接超时”。这种故障通常意味着网络路径不通、防火墙拦截或者服务端根本没在监听。模拟方法很简单在服务端用 iptables 丢弃入站 SYNiptables -A INPUT -p tcp --dport 8080 --syn -j DROP抓包分析时重点看客户端 SYN 的序列号是否每次相同相同说明是重传不是新连接、重传间隔是否符合指数退避、有没有收到任何回应。场景二SYN-ACK 丢失。服务端收到了 SYN回了 SYN-ACK但这个回应在回程路上丢了。客户端视角和场景一一样也是反复重发 SYN。但如果你在服务端抓包会看到服务端其实回了 SYN-ACK而且可能回了多次因为服务端也在重传。这种故障的迷惑性在于客户端和服务端的抓包结果对不上一个说“我发了你没回”一个说“我回了你没收到”。这时候两端同时抓包对比就非常关键。场景三ACK 丢失。这个最隐蔽。服务端收到 SYN回 SYN-ACK客户端收到后回 ACK但 ACK 丢了。服务端会认为握手没完成重传 SYN-ACK客户端认为自己已经 ESTABLISHED 了收到重复的 SYN-ACK 会觉得奇怪。抓包时能看到服务端反复重传 SYN-ACK而客户端可能已经开始发数据了。这种“半开连接”状态是很多诡异问题的根源。3.2 用 tc netem 制造可控的丢包和延迟iptables 只能做“丢”或“不丢”的二元选择想要更精细的控制比如“丢 30% 的包”“延迟 200ms 且抖动 50ms”就得上 tc netem。# 在 eth0 上添加 30% 丢包 tc qdisc add dev eth0 root netem loss 30% # 添加 200ms 延迟抖动 50ms tc qdisc add dev eth0 root netem delay 200ms 50ms # 模拟乱序25% 的包延迟 10ms 发送 tc qdisc add dev eth0 root netem delay 10ms reorder 25% # 用完记得删除否则一直生效 tc qdisc del dev eth0 root这里有个实操心得netem 的丢包是随机的不可复现。同样的配置跑两次丢的包不一样。如果你需要精确复现某个故障场景netem 不太合适得用更底层的手段比如自己写个代理程序按规则丢包。但对于学习抓包分析来说随机丢包已经足够制造出重传、重复 ACK、快速重传这些现象了。我一般会这样设计实验先建立正常连接抓一份“健康”的包作为基线然后加上 netem 丢包再抓一份“故障”的包两份对比着看差异一目了然。没有基线对比你很难判断某个现象是正常的还是异常的。3.3 重传与快速重传的抓包识别TCP 重传分两种超时重传和快速重传。超时重传是发送方等不到 ACK超过 RTO 时间后重发快速重传是发送方收到三个重复 ACK 后立即重发不等超时。在 Wireshark 里超时重传会被标记为TCP Retransmission快速重传标记为TCP Fast Retransmission。但要注意Wireshark 的标记有时候是误报。比如它可能把正常的重传当成异常或者因为抓包点位置不对而误判。判断重传是否真的发生了最可靠的方法是看序列号如果两个包的序列号相同、载荷相同那就是重传。重复 ACK 的识别也有讲究。接收方收到乱序的包时会重复发送最后一个按序到达的 ACK告诉发送方“我还在等某个序列号的包”。收到三个重复 ACK发送方就触发快速重传。抓包时看到连续的、ACK 号相同的包基本就是重复 ACK 了。提示分析重传时把 Wireshark 的“相对序列号”打开默认就是开的比看绝对序列号直观得多。绝对序列号是一串大数字相对序列号从 0 开始一眼就能看出哪个包重传了。3.4 RST 异常的排查思路RST 包是 TCP 里的“急刹车”收到 RST 意味着连接被强制终止。RST 出现的原因很多连接到一个没监听的端口、防火墙主动拒绝、应用层主动关闭、半开连接收到数据等。排查 RST 的第一步是看 RST 是谁发的、在什么阶段发的。如果是握手阶段服务端回 RST说明端口没监听或者被防火墙 REJECT 了。如果是数据传输阶段某一方发 RST可能是应用崩溃或者主动关闭。如果是连接空闲很久后突然 RST可能是中间设备清理了会话表。我遇到过一个很典型的案例客户端连接服务端握手正常发第一个请求也正常但发第二个请求时收到 RST。抓包发现服务端在收到第二个请求后立即回了 RST。查下来是服务端的连接池配置有问题第一个请求处理完后连接被错误地关闭了但客户端不知道继续复用这个连接服务端收到数据后发现连接已经不存在只能回 RST。这种问题不看抓包根本定位不到。4. UDP 故障模拟与抓包实战4.1 UDP 抓包的特殊性UDP 和 TCP 在抓包分析上的最大区别是UDP 没有连接状态没有握手挥手没有重传机制。这意味着你抓到的每一个 UDP 包都是独立的不存在“这个包属于哪个连接”的概念除非应用层自己定义了会话 ID。这给分析带来了两个困难一是无法通过握手判断通信是否正常建立二是丢包后没有任何协议层的补偿只能靠应用层自己处理。抓 UDP 包时过滤规则要更精确。因为 UDP 没有标志位你只能按端口、按 IP、按长度来过滤。我常用的# 按端口抓 tcpdump -i eth0 -w udp.pcap udp port 9999 # 按源或目的 IP 抓 tcpdump -i eth0 -w udp.pcap udp and host 192.168.1.100 # 排除某个端口避免抓到噪音 tcpdump -i eth0 -w udp.pcap udp and not port 53Wireshark 里分析 UDP 时我建议打开“协议层次统计”和“会话统计”能快速看出哪些 IP 对之间流量最大、包数量是否对称。UDP 的故障往往表现为“一方发了很多另一方收得很少”这种不对称在统计图里一眼就能看出来。4.2 丢包、乱序、重复的模拟与识别UDP 的故障模拟和 TCP 类似也是用 tc netem但因为 UDP 没有重传丢包的影响更直接。# 模拟 20% 丢包 tc qdisc add dev eth0 root netem loss 20% # 模拟乱序 tc qdisc add dev eth0 root netem delay 5ms reorder 50% 50% # 模拟重复 tc qdisc add dev eth0 root netem duplicate 10%识别丢包的方法在发送端和接收端同时抓包对比两边的包序列号如果应用层有序列号的话。发送端发了 100 个包接收端只收到 80 个那 20 个就是丢了。如果应用层没有序列号那就只能靠包的数量和内容来推断难度大很多。这也是为什么我强烈建议自研的 UDP 协议一定要在应用层加序列号不然出了问题根本没法排查。乱序的识别接收端收到的包序列号不是递增的比如收到 1、2、4、3、5。Wireshark 本身不会标记 UDP 乱序因为它不知道你的序列号规则你得自己看载荷里的序列号字段。重复的识别同一个序列号的包出现了两次。netem 的 duplicate 是随机复制所以你会看到某些包莫名其妙出现两遍。4.3 MTU 与分片问题的抓包分析UDP 还有一个 TCP 不太会遇到的问题分片。UDP 包如果超过 MTUIP 层会把它分片。分片本身不是故障但分片会带来两个隐患一是任何一片丢失整个 UDP 包就废了而 UDP 不会重传二是某些防火墙或 NAT 设备对分片的处理不一致可能导致分片被丢弃或重组失败。抓包时识别分片看 IP 头的Flags字段MFMore Fragments为 1 表示后面还有分片Fragment Offset不为 0 表示这是后续分片。Wireshark 会把同一个 UDP 包的分片标记出来但需要你手动开启“重组”功能才能看到完整的载荷。我遇到过一个经典问题客户端发一个 2000 字节的 UDP 包服务端收不到。抓包发现包被分成了两片第一片到了第二片丢了。因为 UDP 不重传服务端重组失败整个包被丢弃。解决办法要么是减小包大小避免分片要么是在应用层做分片和重传。这个问题的隐蔽性在于小包测试一切正常一大包就出问题很容易误判为“服务端处理能力不足”。注意路径 MTU 发现PMTUD在 UDP 场景下经常失效因为很多防火墙会丢弃 ICMP 的“需要分片”消息。所以 UDP 应用最好自己控制包大小不要依赖 PMTUD。5. 常见问题与排查技巧实录5.1 抓包抓不到包的几个原因这是新手最常遇到的问题明明配置了抓包但一个包都没有。我总结了几种可能现象可能原因排查方法完全没包网卡选错了ip a确认流量走哪块网卡只有单向包抓包点位置不对在两端同时抓对比包不完整没加-s 0重新抓指定 snaplen过滤太严表达式写错先不加过滤抓确认有流量再逐步加权限不足没用 rootsudo tcpdump还有一个隐蔽的原因流量走了 loopback。如果客户端和服务端在同一台机器上流量走的是lo网卡你在eth0上抓是抓不到的。这个坑我踩过当时以为是防火墙问题查了半天才发现是网卡选错了。5.2 Wireshark 分析中的误报与陷阱Wireshark 很强大但它的自动分析功能有时候会误导人。几个常见的误报校验和错误误报。前面提过网卡卸载会导致 Wireshark 显示校验和错误但实际包是好的。解决办法是关掉校验和验证或者关掉网卡卸载。重传误报。Wireshark 判断重传是基于序列号和时间的如果抓包点位置特殊比如在发送方和接收方之间的中间设备上抓可能会把正常的包误判为重传。乱序误报。多路径或者抓包点不对称时Wireshark 可能把正常到达的包标记为乱序。我的经验是永远不要只信 Wireshark 的标记要自己看序列号、看时间戳、看载荷。标记只是提示最终判断得靠数据本身。5.3 从抓包到定位问题的完整思路抓包只是手段定位问题才是目的。我一般的分析流程是先看整体用 Wireshark 的“统计”功能看包数量、会话分布、协议分布快速定位异常点。再看异常过滤出重传、RST、重复 ACK、ICMP 错误等异常包看它们集中在哪个时间段、哪对 IP 之间。然后看细节针对异常包展开协议树看序列号、标志位、选项字段、载荷内容。最后做对比和正常情况的抓包对比找出差异。没有对比就没有判断。这个流程说起来简单但实际操作中最难的是第 4 步。很多人抓了包看到一堆重传就慌了但其实如果基线抓包里也有重传那说明重传在这个网络环境下是正常的不是故障原因。建立基线这个习惯是我做抓包分析这些年最重要的经验。5.4 几个提升效率的小技巧Wireshark 的“追踪 TCP 流”功能Follow TCP Stream能把一个连接的所有载荷拼起来看分析应用层协议时特别有用。UDP 没有流的概念但可以用“追踪 UDP 流”看一段时间内的所有包。给 pcap 文件命名时带上时间、场景、过滤条件比如20240115_tcp_retrans_loss30.pcap过几天回头看还能想起来是什么实验。分析大文件时先用editcap按时间或包数量切分再逐段分析比一次性打开几百兆的文件流畅得多。如果需要在多台机器上同时抓包用tcpdump的-w写到文件然后统一拉到一台机器上用 Wireshark 的“合并”功能合并注意时间同步问题机器之间时间差太大会导致合并后的时间线错乱。最后分享一个我个人的习惯每次做完故障模拟实验我都会把抓包文件、分析笔记、结论整理成一个文档存档。下次遇到类似问题直接翻文档比重新抓包分析快得多。网络故障这东西太阳底下没有新鲜事你遇到过的大概率别人也遇到过只是你当时没记下来而已。