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)

挥手过程

  1. 客户端发送 FIN 包,进入 FIN_WAIT_1 状态;
  2. 服务器回复 ACK 包,进入 CLOSE_WAIT 状态;
  3. 服务器没有数据需要发送后,发送 FIN 包,进入 LAST_ACK 状态;
  4. 客户端收到 FIN 包后,回复 ACK,进入 TIME_WAIT 的状态;
  5. 服务器收到 ACK 后关闭连接,进入 CLOSED 状态;
  6. 客户端在 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 来判断当前包是之前连接为成功关闭,还是发起了新的连接。