从Wireshark抓包实战看TCP挥手:FIN_WAIT_2状态是如何产生的?

张开发
2026/4/17 15:20:33 15 分钟阅读

分享文章

从Wireshark抓包实战看TCP挥手:FIN_WAIT_2状态是如何产生的?
从Wireshark抓包实战看TCP挥手FIN_WAIT_2状态是如何产生的当你在深夜排查服务器性能问题时突然发现netstat -ant输出中堆积了大量FIN_WAIT_2状态的连接这就像网络协议栈给你留下的神秘线索。本文将带你用Wireshark这把数字显微镜亲手解剖TCP四次挥手过程特别是那个容易被忽视却暗藏玄机的FIN_WAIT_2状态。1. 实验环境搭建与抓包准备我们先来构建一个可重现的实验场景。推荐使用Docker快速部署测试环境既能隔离系统环境又方便重复实验# 启动一个Nginx容器作为服务端 docker run -d --name tcp_test -p 8080:80 nginx:alpine # 在另一个终端启动抓包根据系统选择 # Linux系统 tcpdump -i any port 8080 -w tcp_handshake.pcap # Windows系统需管理员权限 c:\Program Files\Wireshark\tshark.exe -i Ethernet0 -f port 8080 -w tcp_handshake.pcap关键配置细节确保关闭防火墙临时规则sudo iptables -I INPUT -p tcp --dport 8080 -j ACCEPT调整内核参数便于观察实验后请恢复默认echo 300 | sudo tee /proc/sys/net/ipv4/tcp_fin_timeout现在用浏览器访问http://localhost:8080然后立即关闭页面你将在Wireshark中看到完整的TCP生命周期。过滤表达式输入tcp.port 8080可聚焦目标流量。2. 解密四次挥手全流程典型的四次挥手过程如下图所示但真实网络中的数据包交互往往比教科书更复杂客户端 服务端 | | | FIN, ACK | |------------------------------| (客户端进入FIN_WAIT_1) | ACK | |------------------------------| (客户端进入FIN_WAIT_2) | | | FIN, ACK | |------------------------------| (服务端进入LAST_ACK) | ACK | |------------------------------| (客户端进入TIME_WAIT) | |在Wireshark中观察时重点关注三个关键指标Sequence Number确认数据包的连续性Acknowledgment Number验证对方已接收的数据量FlagsTCP控制标志位的组合常见异常场景对比现象正常挥手客户端异常服务端异常FIN_WAIT_2持续时间短暂存在长期堆积不适用根本原因协议标准流程未收到对端FIN服务未正确关闭解决方案无需处理检查客户端代码优化服务端shutdown逻辑3. 深度解析FIN_WAIT_2状态当客户端发送FIN并收到服务端的ACK后便进入这个特殊状态。通过修改内核参数制造一个冻结的FIN_WAIT_2状态# 将FIN_WAIT_2超时设置为1小时仅用于测试 echo 3600 | sudo tee /proc/sys/net/ipv4/tcp_fin_timeout然后在Wireshark中观察你会发现服务端在发送ACK后如果应用层没有主动close连接将保持在CLOSE_WAIT状态客户端则维持在FIN_WAIT_2直到超时或收到对端FIN状态机转换关键路径def tcp_state_machine(): if 主动关闭: send(FIN) - FIN_WAIT_1 receive(ACK) - FIN_WAIT_2 # 这就是我们的焦点状态 receive(FIN) - TIME_WAIT else: receive(FIN) - CLOSE_WAIT send(FIN) - LAST_ACK提示在Linux中实时监控TCP状态变化watch -n 1 netstat -ant | grep -E FIN_WAIT_2|CLOSE_WAIT4. 生产环境问题诊断实战去年我们遇到一个典型案例某电商平台大促期间负载均衡器突然出现大量FIN_WAIT_2状态连接导致新连接被拒绝。通过以下步骤定位问题抓包分析tshark -i eth0 -Y tcp.flags.fin 1 -c 100发现服务端响应FIN后没有继续发送自己的FIN代码检查 发现Node.js服务漏写了response.end()使得连接未能正常关闭临时解决方案# 调整内核参数快速回收连接 sysctl -w net.ipv4.tcp_fin_timeout15 sysctl -w net.ipv4.tcp_tw_reuse1最终修复 在HTTP服务中间件中添加连接超时控制server.on(connection, (socket) { socket.setTimeout(30000); // 30秒超时 });性能优化前后对比指标优化前优化后FIN_WAIT_2数量峰值2.3万100TCP连接建立延迟1200ms200ms错误率5.2%0.1%5. 高级技巧与衍生问题对于需要长期维持连接的场景如WebSocket可以考虑以下优化方案TCP Keepalive配置# 查看当前设置 cat /proc/sys/net/ipv4/tcp_keepalive_time cat /proc/sys/net/ipv4/tcp_keepalive_probes cat /proc/sys/net/ipv4/tcp_keepalive_intvl # 推荐生产环境设置单位秒 echo 1800 /proc/sys/net/ipv4/tcp_keepalive_time echo 3 /proc/sys/net/ipv4/tcp_keepalive_probes echo 30 /proc/sys/net/ipv4/tcp_keepalive_intvl连接状态监控脚本#!/bin/bash while true; do date netstat -ant | awk /^tcp/ {S[$NF]} END {for(a in S) print a, S[a]} ss -s | grep -i wait sleep 5 done在Kubernetes环境中还需要特别注意Pod终止时的优雅退出处理Service的terminationGracePeriodSeconds配置Ingress控制器的连接耗尽机制有一次我们在Istio网格中遇到FIN_WAIT_2堆积最终发现是Envoy在Pod终止时没有正确关闭上游连接。这类问题往往需要结合具体技术栈进行深度排查。

更多文章