3. 分析TCP四次挥手释放连接过程
任务目标
本案例通过抓取一次完整的TCP连接关闭过程,识别四次挥手中的FIN与ACK报文段,结合Wireshark的tcp.flags过滤与TCP状态图,验证主动关闭方进入TIME_WAIT状态。你将使用本地回环地址(127.0.0.1)发起一个可控的TCP连接并立即关闭,从而在单一主机上完成抓包,无需额外服务器。
环境准备
- 操作系统:Windows / Linux / macOS(需支持回环抓包)
- 工具:Wireshark(或tshark命令行版)
- Python 3(用于生成可控的TCP连接与关闭)
抓包与复现步骤
- 启动Wireshark,选择回环接口(
lo或Loopback: lo0),设置抓包过滤器为tcp.port == 54321,点击开始捕获。 - 在终端运行以下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()
- 停止抓包,在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权限)。