网络协议:超时与重传机制

一 TCP 为什么需要重传

因为网络层有可能出现数据包丢失,数据包重复或者无序的情况。为了保证数据传输正确,不重复和有序,所以TCP才提供可靠的数据传输服务。为了保证数据传输的正确性,所以TCP重传它认为已经丢失的包。

TCP如何确认呢?TCP根据接收端返回至发送端的一系列确认信息来判断是否出现丢包。当数据段或确认信息信息丢失,则TCP触发重传操作。

二 重传机制

TCP拥有两套独立机制来完成重传,一是基于时间,而是基于确认信息的构成。

2.1 基于计时器的重传

一旦TCP发送端获取到了基于时间变化的RTT(Round Trip Time:就是数据段发送到收到确认包之间时间)测量值,就可以据此设置RTO(Retransmission Timeout: 重传超时时间,即从发送数据段开始到第一次重传开始之间的时间段)

在TCP发送端需要设置一个计时器以及记录被计时的数据段序列号,如果发送端及时收到了该数据段的确认,那么计时器取消。然后有新的数据段需要发送,在设定一个新的计时器,并记录新序列号。

所以每一个TCP连接的发送端不断地设定和取消一个重传计时器;如果没有数据丢失,则不会出现计时器超时。若在RTO设定的时间内,没有收到该数据段的确认,将会触发超时重传。

一般RTO的设置通常大于RTT(约2倍或者更大),因此基于计时器的重传会导致网络利用率下降。

2.2 快速重传

快速重传是基于接收端的反馈信息来引发重传,而非重传计时器的超时,和超时重传相比,快速重传能够更加及时和有效的修复丢包情况。

当接收端收到比期望序列号大的报文段的时候,便会重复发送最近一次确认的报文段的确认号,我们也称为重复ACK(duplicate ACK)

我们从图中可以接收端并没有收到1461开始的数据段,那么当第一个数据包到达接收端的时候,ack number=1461,这个1461也是接收端期望的报文段序列号,如果不是当新的报文段来的时候,还是返回之前重复的ack number。

如果超时重传定时器超时之前,接受到三个连续的重复ACK,便知道哪一个报文段丢失了,于是重发该报文段,不需要等到超时,大大提高了重传效率。

但是这里还有个问题就是,假设报文段来的时候有顺序问题,比如2021报文段先到来,1461后到来,并没有丢失这个报文段,无非排下顺序就好,所以我们通过一个duplicate ACK并不能确认是发生了丢包还是顺序乱了,所以设置一个阀值,叫做dupthresh。当TCP收到的duplicate ACK个数超过这个dupthresh的时候就认为丢包。默认应该是3,Linux系统可以通过/proc/sys/net/ipv4/tcp_reordering来设置默认值。

2.3 选择确认重传(SACK)

2.3.1 什么是SACK

SACK: Selective Acknowledgement缩写,即选择性确认。指的就是在确认的时候,将对失序数据,只重传丢失的数据,对于没有丢失的数据,我们通过在首部指定其左右边界,就可以在重传的时候,避免再发送一次,提高重传效率。

核心思想: 不重传已经正确接收的数据

那么SACK到底是什么样子呢?

SACK: 包含以下属性

第一:kind, 1个字节

第二:length, 1个字节

第三:left edge(左边界)4个字节

第四:right edge(右边界)4个字节

每一个SACK块都是10个字节

SACK是TCP选项部分的字段,因为TCP选项部分最多可以是40个字节,所以最多只可以有4个SACK块,也就是说最多只允许不重传4个数据段

结构如下:

如上图所示:接收端返回ack numer是4381,因为5841被正确接收,所以我们指明5841这个报文段的它的边界:左边界5841,右边界7301.

2.3.2 为什么使用SACK

我们知道,为了提升效率,我们可以使用快速重传机制,快速重传的核心就是判断接受到的数据段的序列号(seq)和期望的确认号(ack number) 是否一致,如果不一致,则有可能数据丢失或者迟到了,所以设置一个dupthresh阀值,如果超过了则个阀值就认为是数据丢失。

最后返回这个ack number,然后到期后,从这个ack number开始重传之后的报文段,因为可能有些报文段,已经被确认了,所以这会带来性能问题,效率不高。所以就需要一种机制,将只是丢失的数据重传。

2.3.3 SACK重传在接收端和发送端是如何工作的?

接收端:

#1 没当缓存存在失序数据时候,接收端就可以生成SACK.(假设数据丢失,并不是顺序问题)

#2 第一个SACK块里包含最近收到的报文段序列号范围,其余的也是按照接收的先后顺序依次排列

发送端:

发送端应该使用接收端生成的SACK块进行丢失重传,这个过程也叫作选择性重传(selective retransmission)或者(selective repeat)选择性重发。

然后在重传的时候,会根据SACK提供的序列号范围以及ACK信息,避免那些已经被接收端正确接收的数据然后重复发送。当然这也仅限于4个块,多了还是要重复发送。

四 如何确定超时重传时间(RTO)

如果RTO设置较短,有的报文段可能因为网络延迟大而已,这样就会造成不必要的重传;如果设置较大,有可能使得发送端需要等待超时时间过长才会发现数据丢失,影响网络传输效率。

一般来说RTO = RTT + 一部分调整时间

4.1 如何确定RTT时间

4.1.1 通过时间戳选项

#1在发送端发送报文段的时候,把当前时钟放入时间戳字段

#2接收端接受到报文段确认的时候,把这个时间戳复制到确认包中的时间戳

4.1.2 RTT时间调整

如果我们仅用上一次的RTT作为RTO计算标准是否可行? 很明显不好,偶然性太大,比如本来网络好好的,突然这一次发送慢了很多,那如果以这次慢的作为RTT时间,那如果下一次又回到以前水平呢。

所以引入了SRTT(Smooth RTT ),计算公式如下:

SRTT = (α * SRTT) + ( (1 -α) *RTT)

第一次SRTT = 采样的RTT, 后面的采样计算时候SRTT就是前面迭代计算出来的SRTT.

但是这个有个问题,就是在重传的时候的确认的时间是减去这个报文段第一次发送的时间还是重新发送的时间,如果是第一次发送时间,那么RTT值就大了,如果是重新发送时间,又有点小。

所以又有了Karn提出来一个算法,采集RTT的时候直接忽略重传。但是还是有有问题。假设在某个时间,网络突然不好,由快变得很慢,导致所有的很多包都要重传。因为前面一直很通畅,所以必然RTO很小,那么你又说重传的包不参与RTT的采样, RTO永远不会更新,只会不断的重传,情况会越来越糟。而Karn针对这个提出了一个解决方案,只要重传,那么RTO就翻倍,这样就保证了在极端情况下不会导致越来越糟。

但是还是比较不友好的,后面又有人提出了改进方法,也是现在TCP协议使用的方式,新采样的RTT和平滑过的SRTT的差距来作为另一个影响因子。

SRTT = SRTT + α * (RTT - SRTT)

DevRTT = (1-β) * DevRTT + β *(|RTT - SRTT|)

RTO = μ * SRTT + δ * DevRTT

Linux中取值: α是取0.125,β是0.25,μ 是1,δ是4

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

莫言静好、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值