TCP 四次挥手:为什么服务端需要收到客户端的 ACK 后才能 Closed

前言

今天逛牛客网时看到一个帖子,里面提了这样一个问题:

为什么服务端处于 last-ack 后需要收到客户端的 ACK 后才能 closed ?
last-ack 状态下设置一个定时器超时后直接 closed 可以吗?

第一感觉好像可以,不过细想之下就会发现是不可以的。

理由

  1. 如果服务端没有收到第四次挥手的 ACK,那么它没法确认自己第三次挥手的 FIN 有没有被客户端接受;此时假设客户端没有接受到服务端的 FIN 而服务端如问题假设的那样在 last-ack 下超时而关闭了,那么客户端还是处于 FIN_WAIT_1 状态(正常情况下这个状态表示客户端已经关闭连接不能发送数据,但是还在等待接受服务端的数据直到接收到服务端的 FIN),而此时服务端已经 closed,再也不会给客户端发送 FIN,导致客户端在 FIN_WAIT_1 状态下一直等待。

  2. 服务端处于 last-ack 后需要收到客户端的 ACK 后才能 closed:服务端收到客户端第四次挥手的 ACK 意味着服务端第三次挥手的 FIN 被对方成功(客户端可以顺利转移到 TIME_WAIT 状态等待 2MSL 后 closed),如果服务端没收这最后一次挥手的 ACK ,那么它无法确认自己上一次挥手的 FIN 丢失了还是对方回复的 ACK 丢失了,此时就会重发 FIN 直到收到最后一个 ACK。

  3. 如果服务端处于 last-ack 状态后客户端断开连接导致服务端不可能收到第四次挥手的 ACK,这种情况下服务端会重传 FIN 直到重传次数达到内核参数 tcp_orphan_retries(默认值为 8) 后关闭连接

TCP四次挥手图示

小结

思考这种问题要从 TCP 的确认机制和当时处于哪个状态以及该状态的含义出发。
其他类似问题:

  • 第一次握手的 SYN 报文丢失如何处理?

    • 客户端端重传,重传次数由内核参数 tcp_syn_retries(默认值为 5) 规定
  • 第二次握手的 SYN + ACK 报文丢失如何处理?

    • 客户端端重传,重传次数由内核参数 tcp_syn_retries(默认值为 5) 规定;对其而言与第一次握手包丢失一致
    • 服务端端重传,重传次数由内核参数 tcp_synack_retries(默认值为 5) 规定
  • 第三次握手的 ACK 报文丢失如何处理?

    • 服务端重传第二次的 SYN + ACK,或者接收到客户端发送了带有 ACK 的数据包
  • 第一次挥手的 FIN 丢失如何处理?

    • 客户端端重传,重传次数由内核参数 tcp_orphan_retries(默认值为 5) 规定
  • 第二次挥手的 ACK 丢失如何处理?

    • 客户端端重传,重传次数由内核参数 tcp_orphan_retries(默认值为 5) 规定;对其而言与第一次挥手包丢失一致
    • 服务端不会重传,因为丢失的是 ACK 报文(接收方收到 ACK 报文后不会回复确认报文,否则的话就无限循环了)
  • 第三次挥手的 FIN 丢失如何处理?

    • 服务端端重传,重传次数由内核参数 tcp_orphan_retries(默认值为 5) 规定;与客户端重传第一次的 FIN 报文段机制一致
  • 第四次挥手的 ACK 丢失如何处理?

    • 服务端重传第三次挥手的 FIN,客户端此时处于 TIME_WAIT 状态,在 2 MSL 内收到 FIN,重传 ACK,重置定时器

参考