3. 分析TCP四次挥手释放连接过程

分析TCP四次挥手释放连接过程 学习卡片

任务目标

本案例通过抓取一次完整的TCP连接关闭过程,识别四次挥手中的FIN与ACK报文段,结合Wireshark的tcp.flags过滤与TCP状态图,验证主动关闭方进入TIME_WAIT状态。你将使用本地回环地址(127.0.0.1)发起一个可控的TCP连接并立即关闭,从而在单一主机上完成抓包,无需额外服务器。

环境准备

  • 操作系统:Windows / Linux / macOS(需支持回环抓包)
  • 工具:Wireshark(或tshark命令行版)
  • Python 3(用于生成可控的TCP连接与关闭)

抓包与复现步骤

  1. 启动Wireshark,选择回环接口(lo或Loopback: lo0),设置抓包过滤器为tcp.port == 54321,点击开始捕获。
  2. 在终端运行以下Python脚本(它会连接本地54321端口并立即关闭):
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('127.0.0.1', 54321))
s.listen(1)
conn, addr = s.accept()
conn.close()  # 主动关闭连接
s.close()
  1. 停止抓包,在Wireshark显示过滤栏输入tcp.flags.fin == 1 || tcp.flags.ack == 1,观察四次挥手报文。

四次挥手报文识别

正常关闭时,你会看到以下四个报文(以127.0.0.1:54321与客户端端口为例):

  • 第1次:主动关闭方(本例为服务端)发送 FIN, ACK 报文,序列号表示已发送数据的最后一个字节+1。
  • 第2次:被动关闭方回复 ACK,确认号=主动方FIN的序列号+1。
  • 第3次:被动关闭方发送自己的 FIN, ACK(若它没有待发送数据,通常与第2次合并,但标准四次挥手仍分开)。
  • 第4次:主动关闭方回复 ACK,确认被动方的FIN。

在Wireshark中,点击每个报文,观察底部TCP协议详情中的Flags字段,确认FIN与ACK位均为1。主动关闭方在发送第4次ACK后,进入TIME_WAIT状态,持续2个最大报文段生存时间(通常为2分钟,但可在系统参数中调整)。

状态变化验证

在Wireshark中,选择统计 → TCP流图 → 时间序列图(Stevens),可直观看到连接状态从ESTABLISHED到FIN_WAIT_1、FIN_WAIT_2、TIME_WAIT的迁移。若使用Linux,可在抓包同时运行netstat -tulpn | grep 54321观察TIME_WAIT条目。

判定通过条件

  • 抓包文件中存在4个(或3个,若第2、3次合并)携带FIN或ACK标志的报文,且序列号与确认号逻辑自洽(确认号=对方FIN序列号+1)。
  • 主动关闭方在发送最后一个ACK后,本地netstat显示该连接处于TIME_WAIT状态。
  • 若使用tshark命令行验证,可执行:tshark -r capture.pcap -Y "tcp.flags.fin==1" -T fields -e tcp.flags,输出应包含0x011(FIN+ACK)。

常见问题

  • 抓不到FIN包:确认抓包过滤器端口正确,且Python脚本中accept()后确实执行了close()。若使用浏览器等应用,可能因keep-alive机制延迟关闭。
  • 出现RST而非FIN:通常是因为套接字设置了SO_LINGER为0或接收缓冲区有未读数据。本脚本未设置这些选项,应正常产生FIN。
  • TIME_WAIT状态消失过快:Linux默认tcp_fin_timeout为60秒,若需观察更久,可临时执行sysctl -w net.ipv4.tcp_fin_timeout=120(需root权限)。
主动关闭方 (Client/Server) 被动关闭方 (Server/Client) ESTABLISHED ESTABLISHED ① FIN, ACK seq=u, ack=v → FIN_WAIT_1 ② ACK ack=u+1 → FIN_WAIT_2 → CLOSE_WAIT ③ FIN, ACK seq=v, ack=u+1 → LAST_ACK ④ ACK ack=v+1 → TIME_WAIT (2MSL) → CLOSED t0 时间推进 → t4