建立连接定时器 (connection-establishment timer)
重传定时器 (retransmission timer)
延迟应答定时器 (delayed ACK timer)
坚持定时器 (persist timer)
保活定时器 (keepalive timer)
FIN_WAIT_2 定时器 (FIN_WAIT_2 timer)
TIME_WAIT 定时器 (TIME_WAIT timer, 也叫2MSL timer)

建立连接定时器

建立连接的过程中,在发送 SYN 时, 会启动一个定时器(1秒或3秒,取决于实现),如果 SYN 包丢失了, 那么 1 秒以后会重新发送 SYN 包,若再次超时,则会将 RTO 设为 2,再超时则设为 4,每次翻倍(指数回退)。在 Linux 里,客户端的 SYN 报文最大重传次数由 tcp_syn_retries内核参数控制,这个参数是可以自定义的,默认值一般是 5,所以一般等待 60 秒后再无回应就会关闭连接。

重传定时器

重传定时器在 TCP 发送普通报文时设定,具体大小(一般为1秒)参见RTT的测量,在计时器超时后没有收到返回的确认 ACK,发送端就会重新发送队列中需要重传的报文段。使用 RTO 重传计时器一般有如下规则:

  1. 当TCP发送了位于发送队列最前端的报文段后就启动这个RTO计时器;
  2. 收到 ACK 报文后,若所有已发送分组都被确认,则停止计时,若只是部分确认,则重开计时器。
  3. 若超时,则重传所有未完成报文(有待考证)。

对于普通报文的重传定时器,最大重传次数由 tcp_retries2 决定,详细参考此处

延迟应答定时器" blue%}

延迟确认也被称为捎带ACK, 这个定时器在延迟应答的时候使用(最大时间为 0.5 秒)。收到第一个 ACK 报文后,等待一段时间,若最长 0.5 秒内未收到第二个报文,则直接发送 ACK。TCP 最多只能延迟确认一次,即 ACK 报文最多只能累积确认两个报文。

坚持定时器" blue%}

当 TCP 的一端一直接收数据,但是应用层没有及时读取的话,会导致缓存区不断减小,window size 会变为 0,此时我们称呼这个接收窗口为零窗(zero window),对端也不能在发送更多的数据。如果随后本端应用层从 TCP 接收缓存中读取了足够数据,TCP 模块有了足够的新的接收缓存的时候,就会发送一个TCP报文,并带有一个有效非零的 Window size 来指示对端自己已经可以接收新数据了。这个带有有效Window size 的报文我们称为窗口更新(window update)报文。窗口更新报文一般为 pure ACK 报文,不消耗系列号,所以发生丢失并不会进行重传 。如果窗口更新报文发生丢失,那么接收端会等待发送端发送新的数据,而发送端会等待窗口更新报文,这种场景下,两端互相等待对方,就会产生 死锁 (Nagle算法和延迟ACK同时生效的时候也会产生类似的死锁)。为了避免死锁,发送方会设置一个 坚持计时器 ,每当这个定时器超时的时候,发送端就会发送 零窗口探测报文 。RFC1122 建议初始坚持定时器时间为RTO,随后每次超时重传进行指数回退,最大指数回退次数为 tcp_retries2 。达到 tcp_retries2 后不再进行指数回退,且继续以当前 RTO 进行重传。可以看到这里没有释放 TCP 连接,而在 RTO 重传指数回退过程中,当超过根据 tcp_retries2 计算的最大重传时间的时候就会释放TCP连接。

保活计时器" blue%}

在 TCP 连接建立的时候指定了 SO_KEEPALIVE ,保活定时器才会生效 。如果客户端和服务端长时间没有数据交互,那么需要保活定时器来判断是否对端还活着,但是这个其实很不实用,因为默认是 2 小时没有数据交互才探测,时间实在是太长了。如果你真的要确认对端是否活着, 那么应该自己实现心跳包,而不是依赖于这个保活定时器。详细参见此处

FIN_WAIT_2定时器

主动关闭方收到第二次挥手的 FIN 报文后进入 FIN_WAIT_2 状态。由于可能因为网络突然断掉、被动关闭的一段宕机等原因,导致主动关闭的一端不能收到被动关闭的一端发来的 FIN,所以需要 FIN_WAIT_2 定时器进行计时, 如果在该定时器超时的时候,还是没收到被动关闭一端发来的 FIN 那么直接释放这个连接。对于 close() 函数关闭的连接,由于无法再发送和接收数据,所以 FIN_WAIT2 状态不可以持续太久,而 tcp_fin_timeout 控制了这个状态下连接的持续时长,默认值是 60 秒;对于 shutdown() 函数,主动关闭方无法再发送数据,但还可以接收数据,所以可能不会计时,待讨论。

TIME_WAIT定时器

第四次握手后,主动关闭方进入 TIME_WAIT 状态,并等待 2MSL 后关闭。关于为什么需要等待 2MSL ,参见此处MSL 是数据包在网络中的最大生存时间,其值应大于 ip 协议中 TTL 换算的时间,rfc793 建议 MSL 设置为 2 分钟,linux 遵循伯克利习惯设置为 30 s。

文章作者: 极简
本文链接:
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 后端技术分享
网络理论
喜欢就支持一下吧