首先必须明确两个观点:

  • 不是每个数据包都对应一个 ACK 包,因为可以合并确认。
  • 也不是接收端收到数据以后必须立刻马上回复确认包。

如果收到一个数据包以后暂时没有数据要分给对端,它可以等一段时间(Linux 上是 40ms)再确认。如果这段时间刚好有数据要传给对端,ACK 就可以随着数据一起发出去了。如果超过时间还没有数据要发送,也发送 ACK,以免对端以为丢包了。这种方式成为「延迟确认」。

这个原因跟 Nagle 算法其实一样,回复一个空的 ACK 太浪费了。

如果接收端这个时候恰好有数据要回复客户端,那么 ACK 搭上顺风车一块发送。
如果期间又有客户端的数据传过来,那可以把多次 ACK 合并成一个立刻发送出去
如果一段时间没有顺风车,那么没办法,不能让接收端等太久,一个空包也得发。

TCP 要求 ACK 延迟的时延必须小于500ms,一般操作系统实现都不会超过200ms。

延迟确认在很多 linux 机器上是没有办法关闭的

什么时候需要回复 ACK

需要立马回复 ACK 的场景有:

  • 如果接收到了大于一个frame 的报文,且需要调整窗口大小
  • 处于 quickack 模式(tcp_in_quickack_mode)
  • 收到乱序包(We have out of order data.)

其它情况一律使用延迟确认的方式

什么是交互式和 pingpong 呢?

  • 有来有回的双向数据传输就叫 pingpong

延迟确认实际例子演示

除了第一次,剩下的 RTT 全为 40 多毫秒。这刚好是 Linux 延迟确认定时器的时间 40ms 抓包结果如下:

image.png

可以设置关掉延迟确认吗?

大部分 Linux 实现上并没有开关可以关闭延迟确认。

当 Nagle 算法遇到延迟确认

Nagle 算法和延迟确认本身并没有什么问题,但一起使用就会出现很严重的性能问题了。Nagle 攒着包一次发一个,延迟确认收到包不马上回。