sequenceDiagram participant Client participant Server Note over Client: ESTABLISHED Note over Server: ESTABLISHED Client->>Server: FIN (seq=u) Note over Client: FIN_WAIT_1 Server-->>Client: ACK (seq=v, ack=u+1) Note over Server: CLOSE_WAIT Note over Client: FIN_WAIT_2 Server->>Client: FIN (seq=w, ack=u+1) Note over Server: LAST_ACK Client-->>Server: ACK (seq=u+1, ack=w+1) Note over Client: TIME_WAIT Note over Server: CLOSED Note over Client: CLOSED (after 2MSL)
挥手过程
- 客户端发送
FIN
包,进入FIN_WAIT_1
状态; - 服务器回复
ACK
包,进入CLOSE_WAIT
状态; - 服务器没有数据需要发送后,发送
FIN
包,进入LAST_ACK
状态; - 客户端收到
FIN
包后,回复ACK
,进入TIME_WAIT
的状态; - 服务器收到
ACK
后关闭连接,进入CLOSED
状态; - 客户端在
TIME_WAIT
状态等待 2MSL 后,关闭连接。
同时关闭
TCP 的关闭连接不是只能一方发起,双方都可一发起这一过程,如果双方同时发起,在收到对方发送的 FIN
包后,会从 FIN_WAIT_1
变为 CLOSING
状态,发送 FIN
包的 ACK
之后进入 TIME_WAIT
状态,经过 2MSL 之后关闭连接。
为什么需要 TIME_WAIT
- 可靠的实现 TCP 全双工的连接终止(处理最后 ACK 丢失的情况)
- 避免当前关闭连接与后续连接混淆(让旧连接的包在网络中消逝)
减少服务器 TIME_WAIT 的影响
使用 TCP 头部时间戳选项(TCP Options, TSopt),有点像个不会溢出的 SEQ+ACK 双方在通信时不断更新 TSval,TSecr 字段。需要在 TCP 握手时双方同时开启。
有了这个之后 Linux 上可以开启两个选项:
net.ipv4.tcp_tw_reused
net.ipv4.tcp_tw_recycle
这两个选项都依赖 TSopt 来判断当前包是之前连接为成功关闭,还是发起了新的连接。