TCP的重传机制

Last Updated: 2023-05-25 09:18:50 Thursday

-- TOC --

TCP依靠重传来实现在不可靠网络上可靠的数据传输。

TCP实现可靠传输的方式是通过序列号与确认应答。在TCP中,当发送端的数据到达接收主机时,接收端主机会返回一个确认应答消息,表示已收到消息。(实际上,ACK中确认的序列号,表示后续希望接收到的起始序列号)

TCP的重传,包括:

超时重传

数据包丢失,或者ACK丢失的时候,发送方的定时器超时,没有收到ACK,此时重传数据包。(没有人会重传ACK)

两个概念:

当超时时间RTO较大时,重传的发生就慢,丢了老半天才重发,没有效率,性能差。当超时时间RTO较小时,会导致可能并没有丢包就重传,重传的发生虽快,但可能会增加网络拥塞,导致更多的超时,更多的超时导致更多的重发...

精确的测量RTT时间对设定RTO的值是非常重要的,这可让TCP的重传机制更高效。根据上述的两种情况,我们可以得知,超时重传时间 RTO 的值应该略大于报文往返 RTT 的值

具体计算RTO的值还是很复杂的,参考RFC6289,涉及到SRTT概念,平滑的RTT。

RTTM:RTT Measure,表示本次测量的RTT值。

如果超时重发的数据,再次超时的时候,又需要再次重传的时候,TCP的策略是超时间隔加倍。也就是每当遇到一次超时重传的时候,都会将下一次超时时间间隔设为先前值的两倍。两次超时,就说明网络环境差,不宜频繁反复发送。

TCP timestamps (RFC1323)

TCP timestamps的设计目的,是为了记录数据包的发送时间,进而计算RTT,过程如下:

  1. 发送方在发包时,在TCP协议的TSopt选项中,记录当前的发送时间戳,TSval
  2. 接收方收到数据包,进行拆封并把发送的时间戳TSval,复制到返回的ACK报文的TSecr字段;
  3. 发送方收到ack包,用当前时间 - ack中的TSecr时间戳,就可以得到精确的RTT

这个option结构如下:

Kind: 8             // 标记唯一的选项类型,比如window scale是3
Length: 10 bytes    // 标记Timestamps选项的字节数
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| Kind=8 | Length=10 | TS Value (TSval) | TS ECho Reply (TSecr) |
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    1          1             4                       4

tcp_timestamps必须需要双方都要开启方可生效,这是前提条件,如果有一方没有开启则双方进行数据发送接收时该功能不起作用(比如client端发送的SYN包中带有timestamp选项,但server端并没有开启该选项。则回复的SYN-ACK将不带timestamp选项,同时client后续回复的ACK也不会带有timestamp选项。当然,如果client发送的SYN包中就不带timestamp,双向都将停用timestamp)

精确计算RTT

如果没有tcp_timestamps,RTT是如何计算的?

  1. TCP层在发送出一个SKB时,使用skb->when记录发送出去的时间
  2. TCP层在收到SKB数据包的确认ACK时,使用now - SKB->when来计算RTT

但如果发生丢包事件后,该如何计算?

  1. TCP层第一次发送SKB的时间是send_time1, TCP层重传一个数据包的时间是send_time2
  2. 当TCP层收到SKB的确认包的时间是recv_time

但是RTT应该是 (recv_time - send_time1)呢,还是(recv_time - send_time2)呢?

以上两种方式都不可取!因为无法判断出recv_time对应的ACK是确认第一次数据包的发送还是确认重传数据包。因此TCP协议栈只能选择非重传数据包进行RTT采样。但是当出现严重丢包(比如整个窗口全部丢失)时,就完全没有数据包可以用于RTT采样。这样后续计算SRTT和RTO就会出现较大的偏差。

tcp_timestamps选项很好的解决了上述问题,因为ACK包里面带的TSecr值,一定是触发这个ACK的数据包在发送端发送的时间。不管数据包是否重传都能准确的计算RTT(前提是TSecr遵循RTTM中的计算原则)。

当然timestamp不仅解决了RTT计算的问题,还很好的为PAWS机制提供的信息依据。

PAWS

PAWS(Protect Againest Wrapped Sequence numbers),目的是解决在高带宽,高流速情况下,TCP序号重复排列带来的问题。解决这个问题,也是依赖tcp timestamps。

PAWS的做法是,如果收到的一个TCP数据包的timestamp值小于TS.Recnt(接收方最后确认收到的数据的timestampe),则会丢弃该数据包。

数据中心现在普遍100G互联,对应100G网口,只需要0.34秒,就可以完成4294967296字节的发送。这种LFN长肥网络,时延稍微增大,就可能出现相同seq的还在传输中的tcp报文。

>>> 0xFFFFFFFF*8/10**11
0.3435973836

快速重传

TCP还有另外一种快速重传(Fast Retransmit)机制,它不以时间为驱动,而是以数据驱动重传。

fast_retransmit.png

快速重传的工作方式是当收到三个相同的 ACK 报文时,会在定时器超时之前,直接重传丢失的报文段。由于不再等待超时,因此冠以快速。

SACK

快速重传存在一个小问题,发送方在收到三个相同ACK之后,除了ACK直接指向的那个segment,其后的segment是否也要重传呢?发送方无法判断,因此就引入了SACK机制,Selective Acknowledgement,有选择的确认

这种方式需要在 TCP 头部「选项」字段里加一个 SACK 的东西,它可以将缓存的地图发送给发送方,这样发送方就可以知道哪些数据收到了,哪些数据没收到,知道了这些信息,就可以只重传丢失的数据。

此功能默认开启,对应内核参数net.ipv4.tcp_sack

sack.png

DSACK

DSACK,Duplicated SACK,主要作用是使用了 SACK 来告诉「发送方」有哪些数据被重复接收了。

DSACK报文特征是 SACK 中标识的序列号小于ACK确认的序列号,这表示收到了重复数据。此时,发送方可以判断,是对方的ACK报文丢失,或者网络延迟导致超时重发后,对方收到多次,或者网络将发送方的数据包复制了。

此功能默认开启,对应内核参数net.ipv4.tcp_dsack

本文链接:https://cs.pynote.net/net/tcp/202303234/

-- EOF --

-- MORE --