4. TCP 四次挥手原理:FIN、ACK 标志位详解与 TIME_WAIT 状态分析

TCP 连接的建立需要三次握手,而连接的终止则需要四次挥手。这一过程涉及 FIN 和 ACK 两个关键标志位的协同工作,以及一个重要的状态——TIME_WAIT。理解四次挥手的原理,对于分析网络延迟、排查连接泄漏以及设计高并发服务器至关重要。

4.1 四次挥手流程概述

TCP 是全双工通信,因此连接必须由双方各自独立关闭。四次挥手的过程本质上是双方各自发送 FIN 报文并确认对方 FIN 的过程。假设客户端主动发起关闭,其标准流程如下:

  1. 第一次挥手(FIN=1, seq=u):客户端发送一个 FIN 报文,表示客户端不再发送数据,但仍可以接收数据。客户端进入 FIN_WAIT_1 状态。
  2. 第二次挥手(ACK=1, seq=v, ack=u+1):服务端收到 FIN 后,发送 ACK 确认。服务端进入 CLOSE_WAIT 状态。客户端收到 ACK 后进入 FIN_WAIT_2 状态。
  3. 第三次挥手(FIN=1, seq=w, ack=u+1):服务端处理完剩余数据后,发送 FIN 报文,表示服务端也不再发送数据。服务端进入 LAST_ACK 状态。
  4. 第四次挥手(ACK=1, seq=u+1, ack=w+1):客户端收到 FIN 后,发送 ACK 确认。客户端进入 TIME_WAIT 状态,等待 2MSL 后关闭。服务端收到 ACK 后进入 CLOSED 状态。
关键点: 第二次挥手和第三次挥手之间,服务端可能还有数据需要发送,因此 ACK 和 FIN 通常分开发送,这就是“四次”而非“三次”的原因。如果服务端在收到 FIN 时已经没有数据要发送,可以将 ACK 和 FIN 合并,此时退化为“三次挥手”。

4.2 FIN 与 ACK 标志位详解

TCP 报文头部的标志位(Flags)用于控制连接状态。在四次挥手中,FIN 和 ACK 是最核心的两个标志位。

标志位 全称 作用 在挥手过程中的行为
FIN Finish 表示发送方已完成数据发送,请求关闭本方向的连接。 发送 FIN 后,发送方不能再发送数据,但可以继续接收数据,直到收到对方的 FIN。
ACK Acknowledgment 确认收到对方的数据或控制报文。 用于确认收到的 FIN 报文。确认号(ack)为对方 FIN 报文的序列号 + 1。
序列号与确认号: 在挥手过程中,序列号(seq)和确认号(ack)的更新规则与数据传输一致。FIN 报文本身会消耗一个序列号,因此收到 FIN 后,确认号需要加 1。

4.3 四次挥手状态变迁图

下图展示了客户端主动关闭时,双方的状态迁移路径。理解这些状态是排查网络问题的基本功。

TCP 四次挥手状态变迁图 TCP 四次挥手状态变迁图(客户端主动关闭) 客户端 服务端 ESTABLISHED FIN_WAIT_1 FIN_WAIT_2 TIME_WAIT CLOSED ESTABLISHED CLOSE_WAIT LAST_ACK CLOSED ① FIN=1, seq=u ② ACK=1, seq=v, ack=u+1 ③ FIN=1, seq=w, ack=u+1 ④ ACK=1, seq=u+1, ack=w+1 说明:TIME_WAIT 状态持续 2MSL(约 2 分钟),确保最后一个 ACK 能被对方收到,并防止旧连接报文干扰新连接。

4.4 TIME_WAIT 状态深度分析

TIME_WAIT 是四次挥手中最容易被忽视但又最重要的状态。它出现在主动关闭连接的一方(通常是客户端),持续时间为 2MSL(Maximum Segment Lifetime,报文最大生存时间,通常为 2 分钟)。

4.4.1 TIME_WAIT 存在的必要性

  • 确保最后一个 ACK 可靠到达: 如果服务端没有收到第四次挥手的 ACK,会重发 FIN。TIME_WAIT 状态允许客户端在 2MSL 内重新发送 ACK,避免服务端一直处于 LAST_ACK 状态而无法关闭。
  • 防止旧连接报文干扰新连接: 在 2MSL 时间内,网络中可能还存在属于旧连接的延迟报文。TIME_WAIT 确保这些报文在网络中消亡,不会错误地出现在使用相同四元组(源IP、源端口、目的IP、目的端口)的新连接中。
常见问题: 在高并发服务器中,如果客户端(或服务端作为主动关闭方)频繁创建和关闭连接,会导致大量 TIME_WAIT 状态的 socket 堆积。这可能会耗尽系统资源(如端口号),导致无法建立新连接。解决方案包括:调整内核参数(如 net.ipv4.tcp_tw_reuse、net.ipv4.tcp_tw_recycle)或优化应用层连接复用策略。

4.4.2 TIME_WAIT 与 CLOSE_WAIT 对比

状态 出现位置 持续时间 常见原因 排查方向
TIME_WAIT 主动关闭连接的一方 2MSL(约 2 分钟) 正常关闭连接后的标准状态 检查端口是否耗尽;考虑开启 tcp_tw_reuse
CLOSE_WAIT 被动关闭连接的一方 不确定,直到应用层调用 close() 应用层未及时关闭 socket 检查代码中 socket 关闭逻辑;是否存在资源泄漏

4.5 四次挥手抓包验证

使用 Wireshark 或 tcpdump 可以直观地看到四次挥手的过程。以下是一个典型的抓包过滤表达式和结果解读:

# 抓取指定 IP 和端口的 TCP 流量
tcpdump -i eth0 host 192.168.1.100 and port 8080 -w handshake.pcap

# 在 Wireshark 中过滤四次挥手报文
tcp.flags.fin == 1 or tcp.flags.ack == 1

抓包结果中,你会看到以下四个关键报文:

  1. FIN:客户端发送,Flags 为 0x11(FIN + ACK)。
  2. ACK:服务端回复,Flags 为 0x10(ACK)。
  3. FIN:服务端发送,Flags 为 0x11(FIN + ACK)。
  4. ACK:客户端回复,Flags 为 0x10(ACK)。
实战技巧: 如果抓包中只看到三次挥手(即服务端的 FIN 和 ACK 合并为一个报文),说明服务端在收到客户端的 FIN 时已经没有待发送数据,这是一种优化行为,属于正常现象。

4.6 本章小结

四次挥手是 TCP 连接关闭的标准流程,其核心在于 FIN 和 ACK 标志位的配合使用。TIME_WAIT 状态虽然看似“多余”,但它是保证 TCP 可靠性和安全性的关键设计。在实际的链路测试验证中,通过抓包分析挥手过程,可以快速定位连接关闭异常、资源泄漏等问题。