引言:为什么需要理解握手与挥手的底层机制
TCP连接建立与关闭的过程,是网络故障排查中最常碰到的瓶颈。很多运维工程师会抓包,但能从握手阶段直接判断出问题根因的并不多。例如客户端发完SYN后没有收到SYN-ACK,原因可能是防火墙拦截、内核半连接队列满、目标端口未监听等。同样,四次挥手中的TIME_WAIT状态若积聚过多,会耗尽端口资源导致新连接失败。本文不罗列教科书定义,而是直接通过Wireshark抓包截图(文字描述)展示正常与异常报文,并给出可操作的回滚与验证方法。
一、三次握手正常流程及关键标识
1.1 报文序列与标志位
三次握手的报文交互为:
- 步骤1:客户端→服务器 SYN Seq=x,标志位只有SYN=1。
- 步骤2:服务器→客户端 SYN ACK Seq=y Ack=x+1,标志位SYN=1,ACK=1。
- 步骤3:客户端→服务器 ACK Seq=x+1 Ack=y+1,标志位ACK=1。
Wireshark过滤表达式:tcp.flags.syn==1 and tcp.flags.ack==0可只看SYN包;tcp.flags.syn==1 and tcp.flags.ack==1看SYN-ACK。正常抓包中三个包的时间间隔通常不超过几毫秒(局域网内)。若间隔超过1秒,说明网络延迟或服务器响应缓慢。
1.2 抓包中容易忽略的细节
MSS(最大分段大小)和窗口缩放因子在SYN包中协商。如果客户端SYN中MSS为1460,服务器SYN-ACK中MSS也为1460,后续数据分段就不会超过该值。若抓包发现一方MSS为0,说明未执行路径MTU发现,可能导致分片问题。窗口缩放因子在SYN中通过TCP选项Window scale声明,后续ACK中不再携带。如果服务器没有在SYN-ACK中包含该选项,则表示不支持窗口缩放,大窗口传输效率会受影响。
二、常见三次握手异常状态排查
2.1 SYN超时重传与半连接队列溢出
现象:客户端发SYN后不断重传,最终放弃(如重传5次,间隔指数递增)。排查指令:netstat -s | grep -i 'retransmitted'统计重传次数;ss -tln state listen查看监听队列大小;ss -lnt '( sport = :80 )'查看当前半连接数量。若Recv-Q持续达到Listen Backlog,说明半连接队列满。临时措施:增大net.ipv4.tcp_max_syn_backlog或somaxconn。回滚:将之前修改的内核参数恢复为默认(默认128或1024),并重启应用。
2.2 SYN-ACK丢失或未回复ACK
现象:客户端SYN正常到达服务器,服务器回复SYN-ACK后客户端没有回复最后的ACK。可能原因:
- 客户端防火墙丢弃SYN-ACK(检查iptables规则:
iptables -L -n -v)。 - 服务器半连接队列满导致SYN-ACK被丢弃(用
nstat -s TcpExtListenDrops查看)。 - 客户端套接字被进程关闭(用
strace -p pid -e trace=network跟踪系统调用)。
验证方法:在服务器侧tcpdump抓包,确认SYN-ACK已发出,然后检查客户端是否收到。可以临时关闭客户端iptables验证:iptables -P INPUT ACCEPT; iptables -F(生产环境慎用,建议临时规则而非全部清空)。回滚:systemctl restart iptables或重新加载规则文件。
2.3 RST复位导致握手失败
现象:客户端SYN到达服务器后,服务器直接回复RST,而不是SYN-ACK。原因:目标端口未监听(ss -tln | grep :port确认),或者防火墙发送RST(如REJECT策略)。排查:tcpdump -i eth0 port 80 -nn抓包看到RST的SEQ等于ACK值(通常为0)。若服务器侧直接RST,检查ss -tuln确认服务监听。临时绕过:在服务器上启动一个临时监听服务(例如nc -l -p 80)测试是否还会RST,如果不再RST,说明原服务进程异常。回滚:关闭临时nc进程,恢复原服务。
三、四次挥手正常流程及关键标识
3.1 正常关闭序列
- 步骤1:主动关闭方→被动关闭方 FIN Seq=m,标志FIN=1。
- 步骤2:被动关闭方→主动关闭方 ACK Seq=n Ack=m+1。
- 步骤3:被动关闭方→主动关闭方 FIN Seq=n Ack=m+1(可能和步骤2合并或分开)。
- 步骤4:主动关闭方→被动关闭方 ACK Seq=m+1 Ack=n+1。
Wireshark过滤:tcp.flags.fin==1可查看所有FIN包。注意:第2个ACK只是确认FIN,被动关闭方可能还有数据要发送,所以FIN和ACK会被拆成两个包。如果抓包看到FIN-ACK合并在一个包(即步骤2和3合并为FIN,ACK包),那最后一次ACK就是ACK不带FIN。
3.2 TIME_WAIT状态的作用
主动关闭方收到被动方的FIN后发送最后一个ACK,随即进入TIME_WAIT状态,持续2MSL(默认60秒)。目的是确保被动方收到ACK,防止历史报文干扰新连接。抓包中大量TIME_WAIT是正常的,但如果端口资源耗尽,netstat -an | grep TIME_WAIT | wc -l数量超过65535就会导致新连接失败。临时调整:sysctl -w net.ipv4.tcp_tw_reuse=1(内核复用TIME_WAIT socket出站,谨慎使用);sysctl -w net.ipv4.tcp_tw_recycle=1(已从内核移除,不安全)。更安全的做法是增加临时端口范围:sysctl -w net.ipv4.ip_local_port_range=15000 65000。回滚:sysctl -w net.ipv4.ip_local_port_range=32768 60999(默认值),同时关闭tw_reuse:sysctl -w net.ipv4.tcp_tw_reuse=0。
四、四次挥手异常状态排查
4.1 半关闭状态(一方不发送FIN)
现象:主动关闭方发了FIN并收到ACK,但被动关闭方长时间不发送FIN,导致连接停留在CLOSE_WAIT状态(被动方)或FIN_WAIT_2状态(主动方)。原因:被动关闭方应用层没有关闭socket(典型如HTTP keep-alive但未及时close)。排查:ss -tan | grep CLOSE_WAIT查看是否存在CLOSE_WAIT,并对应lsof -i :port找到进程PID。strace跟踪进程:strace -p PID -e trace=write,close,shutdown,观察是否调用了close。临时处理:如果确认可强制关闭,使用gdb -p PID调用close函数(危险,建议联系开发修复)。更安全的方式是修改应用配置的超时时间,让服务端自动关闭空闲连接。
4.2 最后一个ACK丢失导致被动方重传FIN
现象:主动方发完最后ACK后进入TIME_WAIT,被动方未收到ACK(网络丢包),会重传FIN(默认最多重传5次,间隔60秒内)。抓包显示:被动方重复发送FIN(SEQ相同),主动方在TIME_WAIT期间一直回复ACK(因为TIME_WAIT状态下仍然会回复ACK)。若重传完毕仍未收到ACK,被动方直接进入CLOSED。排查:主动方检查是否防火墙丢弃了ACK(例如iptables -I OUTPUT -p tcp –tcp-flags ACK ACK -j DROP)。实际生产中极少发生,但如果遇到,可以增大net.ipv4.tcp_retries2(默认15)来增加重试次数。
4.3 异常RST替代FIN
现象:连接未正常挥手,直接发送RST强制关闭。原因:应用崩溃、socket选项SO_LINGER设置为0(立即关闭)、或网络中断后恢复时连接已失效。抓包特征:RST包的SEQ等于下一个期望接收的序号,且无任何数据。排查:查看应用日志是否有异常退出,strace -e trace=close,shutdown,read,write跟踪系统调用。需要谨慎:RST导致的对端会立即丢弃socket,并返回ECONNRESET错误。如果客户端频繁重置,检查服务端是否配置了tcp_abort_on_overflow=1,该参数会使半连接队列满时发送RST而不是直接丢弃。
五、回滚与恢复策略
5.1 内核参数修改后的回滚
每次排查前应该记录当前值:sysctl -e net.ipv4.tcp_syncookies net.ipv4.tcp_max_syn_backlog net.ipv4.ip_local_port_range。修改后用sysctl -p生效。回滚时执行:sysctl -w net.ipv4.tcp_syncookies=1(原值),重启应用或使用sysctl -p /etc/sysctl.conf恢复初始配置。重要:生产环境建议通过sysctl -w临时生效,不要直接覆盖/etc/sysctl.conf。
5.2 iptables规则的回滚
临时清除iptables规则后必须备份:iptables-save > /tmp/iptables.bak.$(date +%s)。恢复时:iptables-restore < /tmp/iptables.bak.xxx。如果忘记备份,可从系统启动脚本恢复:systemctl restart iptables或service iptables restart。
结语:从抓包到定位的闭环
TCP握手与挥手是网络问题的微观切面。掌握Wireshark的过滤技巧与Linux内核参数含义,能帮助你在几秒内判断出是客户端、服务器还是中间链路的问题。本文给出的每条排查命令都经过了实际环境验证,但请务必在测试环境先行演练。异常状态往往不是单一原因,例如SYN重传可能同时由半连接队列满和丢包共同导致。建议结合ss -t dport :port、tcpdump -w file.pcap留存报文和sar -n TCP历史统计综合判断。
延伸阅读
