TCP 连接关闭
TCP 两个发送方向独立关闭。本课展示常规四段流程:FIN、ACK、FIN、ACK。主动关闭者走 FIN_WAIT_1→FIN_WAIT_2→TIME_WAIT→CLOSED,被动关闭者走 CLOSE_WAIT→LAST_ACK→CLOSED。
一方说“不再发送”,另一方先确认,却可能还有内容要发;等它也完成,再发送自己的 FIN。主动关闭者最后保留 TIME_WAIT,等待 2·MSL,以处理重传 FIN 并让旧报文过期。
关闭如何工作
客户端在本例主动关闭发送方向,发送 FIN seq=u。服务端收到后可先发 ACK ack=u+1,自己的发送方向仍可继续,这就是半关闭。服务端稍后完成,再发 FIN seq=w, ack=u+1,客户端确认 ack=w+1。
FIN 和 SYN 一样占一个序列号,即使没有载荷。ACK 表示下个期待号,因此加 1。这里用 u、w 代替实际三十二位序列空间。
中间 ACK 与第二个 FIN 不一定同时准备好,所以展示四段;若被动方也已准备关闭,可以合并,不能说每次挥手必定四个独立报文。应用若要保留读取侧,实际 socket API 常使用 shutdown 等半关闭操作;close 的资源行为依平台而定。
逐步演示
Normal close 共十三步:
- 双方 ESTABLISHED,客户端应用发起关闭,成为主动方。
- 客户端发第 1 段
FIN seq=u,变 FIN_WAIT_1。 - 服务端收到,知道客户端发送结束,变 CLOSE_WAIT。
- 服务端发第 2 段
ACK ack=u+1。 - 客户端收到,FIN_WAIT_1→FIN_WAIT_2,客户端发送方向已确认关闭。
- 半关闭窗口,服务端仍可有数据要交付;先前 ACK 与尚未发出的 FIN 分开。
- 服务端结束发送,发第 3 段
FIN seq=w, ack=u+1,CLOSE_WAIT→LAST_ACK。 - 客户端收到 FIN,FIN_WAIT_2→TIME_WAIT。
- 客户端发第 4 段
ACK ack=w+1。 - 服务端收到,LAST_ACK→CLOSED。此场景它先结束。
- 客户端保持 TIME_WAIT 共 2·MSL,可确认重传 FIN,同时等待旧重复段过期。
- 计时到期,TIME_WAIT→CLOSED。
- 两条状态阶梯都在 CLOSED,正常关闭结束。
卡片穿过生命线,状态变化留下标签,旧标签淡化。报文传输与 TIME_WAIT 倒计时放慢,便于看 seq、ack 与状态的对应。
TIME_WAIT 的作用
纯 ACK 本身不会再被 ACK 确认,最后这个也一样。因此客户端不知道最终确认是否到达。
Lost final ACK 丢掉 ack=w+1。服务端留在 LAST_ACK,重传计时到期后再发 FIN seq=w。客户端仍在 TIME_WAIT,重发 ACK ack=w+1,并重新开始 2·MSL。服务端这次收到后 CLOSED,客户端等新计时结束。最终相同,多一次重传与确认。
若客户端发完最后 ACK 就销毁状态,后续 FIN 可能落到无连接状态,得到 RST 而非正常再确认,失去本应保留的正常关闭信息。2·MSL 也让旧段在同一连接标识再次使用前消退;它不是任意网络延迟下绝对不会丢包的保证。完整状态与计时规则见 RFC 9293。
边界情况
- 最后 ACK 丢失:LAST_ACK 重发 FIN,TIME_WAIT 再确认并重启计时。
- 同时关闭:双方先发 FIN,再见对方 FIN,可走 FIN_WAIT_1→CLOSING→TIME_WAIT,两方都等待;本课未动画展开。
- 对方迟迟不发 FIN:主动侧可停在 FIN_WAIT_2。真实栈可能对已无应用持有的连接做超时清理,具体依实现,不能假定所有半关闭都同一超时。
- RST 中止不走本课完整正常关闭路径,未交付数据可能丢弃,语义不同于有序 FIN 结束。
常见错误
- 期望一定三段,忽略被动方的发送可能尚未完成;也不能反过来断言 ACK 与 FIN 永远不能合并。
- 认为双方同时 CLOSED。默认服务端收到最后 ACK 先关闭,客户端还在 TIME_WAIT。
- 把 TIME_WAIT 当纯浪费。它支持最终确认重发与旧段隔离,主动关闭大量连接的服务端也可能积累该状态。
- 把 TIME_WAIT 固定归客户端,或声称永远只有一方。默认归主动关闭者;同时关闭两端都可进入。
- 漏掉 FIN 消耗序号,应为 u+1、w+1。
模型范围
使用符号序号,不穿插数据报文,把重传与等待压成教学动作。只展示常规主动/被动关闭与最终 ACK 单次丢失,不涵盖全部状态组合、资源回收或协议栈优化。