我们已经准备好了,你呢?

2025我们与您携手共赢,为您的企业形象保驾护航!

TCP 基础篇-第五篇:TCP 连接断开——四次挥手全解析

深入理解 TCP 的优雅关闭:状态迁移、TIME_WAIT 与常见陷阱

在 TCP 协议的学习中,三次握手往往被反复强调,但连接断开的过程——即“四次挥手”——同样至关重要。如果说握手是建立信任的仪式,那么挥手则是得体告别的艺术。不同于握手的三次交互,断开连接需要四次报文交换,这背后隐藏着 TCP 全双工通信的本质:每个方向都需要独立关闭。本文将逐帧拆解四次挥手的每个步骤,剖析状态机变迁,探讨 TIME_WAIT 与 CLOSE_WAIT 这两个“老大难”问题,并给出最佳实践建议,帮助你彻底掌握 TCP 连接终止的精髓。

一、为什么需要四次挥手?

TCP 提供全双工通信,意味着数据可以同时在两个方向上传输。因此,当一方想要终止连接时,它必须确保自己不再发送数据,同时也要确认对端已经完成数据发送。这就像两个人在电话交谈中,一方说“我说完了”(FIN),另一方可能回应“好的,我知道了”(ACK),但此时另一方可能还有话要说,所以它会在自己说完后再发送自己的“我说完了”(FIN),然后最初的一方再确认(ACK)。因此,每个方向都需要一个 FIN 和一个 ACK,总计四次报文。

对比三次握手(SYN, SYN-ACK, ACK),断开之所以多一次,是因为握手时双方可以同时交换 SYN(即 SYN 与 ACK 合并),而断开时,两个方向的 FIN 通常不能合并,因为双方可能在不同时刻完成数据发送。当然,如果双方同时发送 FIN(即同时关闭),则可能变为四次甚至三次,但常规场景下是四次。

二、四次挥手的标准流程

假设客户端(主动关闭方)发起断开请求,服务器(被动关闭方)响应。实际中任何一端都可以主动关闭,但流程对称。以下以客户端主动关闭为例:

  • 第一次挥手(FIN):客户端应用调用 close() 或 shutdown(SHUT_WR),TCP 层发送一个 FIN 报文段,序列号为 u(等于之前已发送数据的最后一个序列号加 1)。此时客户端进入 FIN_WAIT_1 状态,表示等待对方的 ACK。

  • 第二次挥手(ACK):服务器收到 FIN 后,发送 ACK 报文,确认号为 u+1,同时服务器进入 CLOSE_WAIT 状态。此时客户端收到 ACK 后进入 FIN_WAIT_2 状态,等待服务器发送它的 FIN。此时连接处于“半关闭”状态:客户端不再发送数据,但可以接收数据;服务器仍可发送数据。

  • 第三次挥手(FIN):当服务器应用也决定关闭连接(调用 close()),服务器发送 FIN 报文,序列号为 w(服务器已发送数据的最后一个序列号+1),并进入 LAST_ACK 状态,等待客户端的最终 ACK。

  • 第四次挥手(ACK):客户端收到 FIN 后,发送 ACK 确认,确认号为 w+1,然后客户端进入 TIME_WAIT 状态,等待 2MSL(最大报文生存时间)后进入 CLOSED。服务器收到 ACK 后立即进入 CLOSED 状态。

客户端状态: ESTABLISHED → FIN_WAIT_1 → FIN_WAIT_2 → TIME_WAIT → CLOSED 服务器状态: ESTABLISHED → CLOSE_WAIT → LAST_ACK → CLOSED

值得注意的是,TIME_WAIT 只在主动关闭方出现(通常是客户端,但服务器也可能主动关闭)。2MSL 的等待是为了确保最后一个 ACK 能到达对端,如果该 ACK 丢失,对端会重传 FIN,主动关闭方可以重发 ACK,同时防止旧连接的延迟报文干扰新连接。

三、状态机详解:从 FIN_WAIT_1 到 TIME_WAIT

四次挥手涉及多个 TCP 状态,每个状态都有其特定的意义和超时处理:

  • FIN_WAIT_1:主动关闭方发送 FIN 后的初始状态。如果在此状态下收到对端的 FIN+ACK(即同时关闭),则可能直接进入 TIME_WAIT

  • FIN_WAIT_2:收到对端对 FIN 的 ACK 后进入。此时主动方已无数据发送,但可以接收数据。如果对端一直不发送 FIN,则可能因 tcp_fin_timeout 超时而强制关闭。

  • CLOSE_WAIT:被动关闭方收到 FIN 并发出 ACK 后进入。此状态表示对端已关闭发送,但本端仍可发送数据。应用层需要适时调用 close() 来发送 FIN,否则连接会一直停留在 CLOSE_WAIT,造成资源泄漏。

  • LAST_ACK:被动方发送 FIN 后等待主动方的最终 ACK。收到 ACK 后进入 CLOSED

  • TIME_WAIT:主动方发送最终 ACK 后进入,持续 2MSL(通常为 1~4 分钟)。这是为了保障连接的可靠关闭,并防止“旧连接数据”污染“新连接”。

⚠️ 常见误区: 许多开发者以为 close() 会立即关闭连接,但实际上它只是触发 FIN 发送,真正的关闭需要等待四次挥手完成。如果使用 SO_LINGER 零超时,则会发送 RST 强制关闭,绕开四次挥手,但一般不推荐。

四、TIME_WAIT 的奥秘与调优

TIME_WAIT 是 TCP 协议中最容易引发问题的状态之一。它的存在有两大理由:

  1. 确保最后的 ACK 能被对端收到:如果主动方发送的 ACK 丢失,被动方会重传 FIN,主动方必须能在 2MSL 内收到并重发 ACK,否则被动方无法正确关闭。

  2. 让延迟的重复报文自然消失:网络中可能存留旧连接的延迟数据包,如果立即建立新连接且使用相同的四元组(源IP、源端口、目标IP、目标端口),这些旧包可能被误认为是新连接的数据。2MSL 时间足以保证旧包在网络中被丢弃。

然而,在高并发服务器(如 Web 服务器)中,如果服务器主动关闭连接(例如短连接场景),大量 TIME_WAIT 会耗尽端口资源(因为端口号有限)。常见调优手段包括:

  • 开启 tcp_tw_reuse(Linux 3.7+)允许在安全条件下重用 TIME_WAIT 连接,但需配合时间戳。

  • 调整 tcp_tw_recycle(已被废弃,且 NAT 下有问题,不建议使用)。

  • 调整 tcp_max_tw_buckets 限制最大 TIME_WAIT 数量,超出后直接释放并记录警告。

  • 应用层设计上尽量让客户端主动关闭,避免服务器产生大量 TIME_WAIT。

在实践中,对于短连接服务(如 HTTP/1.0),服务器经常处于 TIME_WAIT 状态,可通过长连接(Keep-Alive)或协议升级(HTTP/2)来减少连接建立/断开的频率。

五、CLOSE_WAIT 泄漏:如何识别与修复

CLOSE_WAIT 是另一个常见的异常状态。它表示对端已经关闭发送(收到 FIN),但本端应用尚未调用 close()。如果大量连接堆积在 CLOSE_WAIT,说明应用代码存在资源泄漏——可能是忘记关闭套接字,或是业务逻辑阻塞导致未能及时处理关闭事件。排查方法:

  • 使用 netstat -ant | grep CLOSE_WAIT 观察数量。

  • 检查应用代码,确保每个 accept() 返回的 socket 或每个连接对象在完成通信后都正确关闭。

  • 在服务端合理设置 SO_RCVTIMEO 或 SO_SNDTIMEO,防止长期阻塞。

如果 CLOSE_WAIT 持续增长,通常意味着程序有 bug,需要从代码层面根治,单纯调整内核参数无效。

六、同时关闭(Simultaneous Close)

当双方同时调用 close() 时,可能出现同时发送 FIN 的情况。此时,双方都会进入 FIN_WAIT_1 状态,收到对方的 FIN 后,各自发送 ACK,并进入 CLOSING 状态(而不是 FIN_WAIT_2),随后再收到 ACK 后进入 TIME_WAIT。虽然这种情况较少见,但 TCP 状态机对此有完善支持,确保最终都能正确关闭。

七、挥手过程中的异常处理

网络不可靠,挥手过程中可能发生丢包。TCP 通过超时重传机制来应对:

  • 如果 FIN 丢失,发送方会重传 FIN(重传计时器)。

  • 如果第二次的 ACK 丢失,主动方会重传 FIN(因为收不到 ACK),被动方会重复 ACK。

  • 如果最终的 ACK 丢失,被动方(LAST_ACK)会重传 FIN,主动方(TIME_WAIT)收到后重发 ACK,并重置 TIME_WAIT 计时器。

  • 如果主动方在 TIME_WAIT 期间收到 FIN,说明对端未收到 ACK,需要重新发送 ACK。

这些机制保证了即使在丢包环境下,连接也能最终关闭,不会永久卡住。

八、最佳实践:优雅关闭的几点建议

  • 优先让客户端主动关闭,避免服务端大量 TIME_WAIT 占用端口。

  • 使用连接池复用长连接,减少频繁握手/挥手开销。

  • 监控系统状态,特别是 netstat 中的 TIME_WAIT 和 CLOSE_WAIT 计数,设置告警阈值。

  • 在代码中正确处理关闭异常,如捕获 ECONNRESET 等错误,避免因 RST 导致未完成四次挥手。

  • 谨慎使用 SO_LINGER,除非明确需要快速释放资源并接受数据丢失风险。

结语:挥手是仪式的终点,也是新连接的起点

四次挥手虽然比握手多一次交互,但每一步都蕴含深刻的工程设计智慧:全双工独立关闭、状态容错、延迟报文隔离——这些都是 TCP 能够成为互联网基石的重要原因。理解挥手过程,不仅能帮助你写出更健壮的网络程序,还能在排查连接泄漏、端口耗尽等问题时迅速定位根源。至此,我们已完成了 TCP 连接建立(三次握手)和断开(四次挥手)的完整闭环,下一篇章我们将进入 TCP 的“异常断链”专题——RST 报文,敬请期待!


滕州市启明星网络科技有限公司——10年专注滕州本地建站服务,提供企业官网定制、网店代运营、SEO优化一站式解决方案。从策划到落地,从上线到推广,我们让您的每一分投入都转化为看得见的商机。 如果您有网站建设,网站改版,域名注册,主机空间,手机网站建设,网站备案,电商托管,抖音运营,短视频营销,SEO优化,GEO优化等方面的需求,请拨打咨询热线: 18866698839,免费获取专属方案,我们将成为您创业路上的技术合伙人,助力打造低成本、快部署、强转化、可成长的线上业务阵地。

我们已经准备好了,你呢?

2025我们与您携手共赢,为您的企业形象保驾护航!