TCP 四次挥手:连接是怎么关闭的
更新时间:2026-09-17。网络协议域·传输层。完整状态迁移见 TCP 状态机,TIME_WAIT 的内核参数调参见 TIME_WAIT 原理。
TCP 是全双工的:两个方向的数据流要各自独立关闭,所以正常关闭需要四个报文(四次挥手)。FIN 表示"我没有数据要发了",但还能继续接收对方的数据。
一、四个报文
主动关闭方 被动关闭方
ESTABLISHED ESTABLISHED
|------ FIN, seq=u --------->| -> CLOSE_WAIT;主动方 FIN_WAIT_1
|<----- ACK, ack=u+1 --------| 主动方 FIN_WAIT_2
(被动方应用可能还在发剩余数据……)
|<----- FIN, seq=v ----------| -> LAST_ACK;主动方 -> TIME_WAIT
|------ ACK, ack=v+1 ------->| 被动方 CLOSED
TIME_WAIT(2MSL)-> CLOSED- 主动方发 FIN(自己没有新数据要发)。
- 被动方立刻回 ACK 确认——但这只是确认了"对方方向关了"。
- 被动方等自己应用把剩余数据发完、调用 close 后,再发自己的 FIN。
- 主动方回 ACK,被动方收到即 CLOSED;主动方进入 TIME_WAIT 等 2MSL。
二、为什么挥手是四次,握手只要三次
握手里服务端的 ACK 和 SYN 可以合并成一个 SYN+ACK;挥手时被动方收到 FIN 后回的 ACK,与它自己的 FIN 往往不能合并:收到 FIN 只代表对方不发了,而本端可能还有数据没发完,内核只能先 ACK、等应用真正 close 时才发 FIN。于是 ACK 与 FIN 分成两个报文,形成四次。
当被动方也正准备关闭、ACK 与 FIN 之间没有数据要发时,某些实现(开启 delayed ACK 或正好关闭)会把 ACK 与 FIN 合并,抓包可能看到"三次",但协议逻辑仍是两个方向各自关闭。
三、TIME_WAIT:主动方的 2MSL
主动关闭方发完最后 ACK 后进入 TIME_WAIT,停留 2MSL(Linux 约 60 秒):
- 保证最后 ACK 可达:若 ACK 丢失,被动方会重传 FIN,主动方还在 TIME_WAIT 就能重发 ACK;若已 CLOSED 则回 RST。
- 让旧报文消亡:保证该四元组上一轮连接的延迟报文在网络中过期,不污染新连接。
工程推论:
- TIME_WAIT 永远出现在主动关闭方。想把 TIME_WAIT 推给客户端,服务端可配置优雅关闭时不主动关连接。
- 它不占 fd、不是泄漏;高并发短连接海量 TIME_WAIT 正常,仅在本地临时端口/conntrack 受限时用
tcp_tw_reuse、扩ip_local_port_range(见网络连接数 FAQ、TIME_WAIT 调优)。 - 不要用已废弃的
tcp_tw_recycle(与 NAT 冲突会丢连接)。
四、CLOSE_WAIT 堆积:应用没关套接字
被动方收到 FIN、内核已回 ACK,进入 CLOSE_WAIT,等待应用层调用 close() 发出自己的 FIN。若大量连接长期停在 CLOSE_WAIT:
- 含义:对端早就想断开,但本端应用一直没 close——典型的连接泄漏或异常路径没释放(没在 finally/错误处理里关连接、线程池阻塞没人处理)。
- 危害:占用文件描述符,最终
too many open files。 - 排查:
ss -tan state close-wait看数量与程序,lsof -p PID看持有的 fd,修代码确保关闭。内核参数救不了 CLOSE_WAIT。
对比记忆:TIME_WAIT 多 = 正常协议态(主动关方);CLOSE_WAIT 多 = 程序 bug(被动方不关)。
五、RST:跳过四次挥手的异常关闭
并非所有关闭都走 FIN 四次。收到 RST(复位)会立即释放连接、丢弃队列数据:
- 访问没有监听的端口、连接已不存在仍收到数据、半开连接(对端宕机重启)。
- 设置
SO_LINGER为l_onoff=1,l_linger=0后 close 会直接发 RST,可主动避免 TIME_WAIT(代价是可能丢弃未发数据,慎用)。 - RST 过多往往意味着应用层或网络异常,抓包时值得关注。
相关阅读
TCP 状态机 串起全部状态,三次握手 讲建立,TIME_WAIT 调参见 concepts/network/tcp-timewait,fd 上限见网络连接数 FAQ。
总结
四次挥手是 FIN/ACK/FIN/ACK 两个方向各自关闭:被动方先 ACK 再等数据发完才发 FIN,所以比三次握手多一个报文;主动方在 TIME_WAIT 停 2MSL 保证 ACK 可重传、旧报文过期(正常、无害),被动方卡在 CLOSE_WAIT 则是应用没 close 的真 bug,RST 是跳过挥手的异常释放。