TCP确保可靠的机制——重传,滑动窗口,拥塞窗口,流量控制,延迟确认

确保TCP传输可靠的机制

为了保证TCP传输的可靠性,需要防止数据的破坏,丢包,重复以及分片乱序等问题.为此引入了众多机制.

重传机制

在TCP中,通过序列号和确认应答来保证数据被对方接收到.发送方发送的请求报文中会携带序列号,接收方为了让发送方知道自己已经接收到信息,会返回一个确认应答,其中报文中的确认序号为请求报文中序列号+1.

但在传输过程中,由于网络的收敛,数据包很可能发生丢包.因此引入重传机制

超时重传

在发送方发送数据时设定一个计时器,当超过指定的时间后,仍没有接收到ACK报文,则重传请求报文.这就是超时重传

触发超时重传的有2种情况

  1. 发送的请求报文没有到达对方
  2. 对方发送的ACK报文没有接收到

对于超时重传机制,最为关键的是确定超时重传的时间.也就是RTO.

在介绍RTO之前,先介绍RTT(往返时延).RTT指的是发送请求到接收到请求的确认应答这一整个过程耗费的时间.

当RTO较长时,我们会浪费时间,降低了网络传输效率;当RTO较短时,重传的报文可能在接收到ACK报文前就被发送,则会造成不必要的重传报文的发送.

因此可以得出结论:RTO应该略高于RTT.但RTT的值是受网络质量影响而不断变化的,因此RTO也是一个动态值.

Linux计算RTO的流程

估计往返时间:

  1. 利用TCP采样RTT的时间,然后进行加权平均,得到一个平滑的RTT的值.
  2. 采样RTT的波动范围,避免RTT发生大的波动后无法被监测到.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pSb4NSe0-1661958443457)(C:\Users\qiu\AppData\Roaming\Typora\typora-user-images\1659667031510.png)]

其中SRTT是平滑RTT,DevRTT是计算平滑RTT与最新RTT的差距.

在Linux中,α = 0.125,β = 0.25, μ = 1,∂ = 4

在重传后之后每次重传的时间间隔都是前一次重传时间间隔的2倍

快速重传

超时重传面临的一个问题就是超时重传等待的时间较长,为了更快的重传,引入了快速重传机制

快速重传机制不以时间为驱动,而是以数据驱动为驱动.[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-twftg2Wp-1661958443458)(C:\Users\qiu\AppData\Roaming\Typora\typora-user-images\1659667371899.png)]

如上图,在发送数据时,由于数据报文(Seq=2)的丢失,导致其之后的确认应答的报文都是数据报文(Seq=2)的确认应答报文,当发送方收到三个同样的ACK时就会触发重发机制.

快速重传解决了重传周期长的问题,但其面临一个新的问题:重传1个还是重传所有?

假设发送方发送的数据报文(Seq=2)和数据报文(Seq=3)都丢失,在之后发送方收到的Ack都是数据报文(Seq=2)的Ack,此时如果发送方只重传Seq=2的数据报文,那么Seq=3的数据报文还需要在之后等待3个Ack之后再重传,重传的效率很低;而如果重传所有报文(Seq=26),Seq(46)的报文已经被服务器成功接收过1次,相当于重复发送了相同的请求,浪费了资源.

为了解决快速重传几个的问题,引出了SACK方法.

SACK方法

SACK:选择性确认(Selected Acknowledge)

这种重传方式需要在TCP头部[选项]字段里加一个SACK,它可以将已收到数据的信心发送给发送方,通过SACK发送方就可以知道哪些数据收到了,哪些数据没有收到.这样发送方就可以将需要重传的所有数据一次性重传.

SACK要求通信双方都支持才可以正常使用.Linux中通过net.ipv4.tcp_sack参数开启.

Duplicate SACK

Duplicate SACK又称为D-SACK,主要作用是告诉发送方哪些数据被重复接收了.

发送重复数据情况:

  1. 接收方的ACK报文丢失,发送方触发重传机制重传相同的报文
  2. 网络收敛导致之前发送的请求报文没有达到接收端,之后发送方重传相同报文时之前没有到达的请求报文也到达了接收端.

当接收方发现接收了相同的数据后,就会利用D-SACK告知发送方哪个段的数据已经接受过了.

通过ACK与SACK比较可以得知重传的具体原因是请求报文丢失(ACK与SACK一致)还是确认应答报文丢失(ACK>SACK且触发的重传是超时重传)还是因为请求报文被网络延迟(ACK>SACK且触发的重传是快速重传)了.

在Linux中可以通过net.ipv4.tcp_dsack参数开启.

滑动窗口

在通信过程中,发送方每发送一个请求给接收方,需要接收到接收方的确认应答后才能继续发送下一个请求.这种通信方式很容易造成队头阻塞.也就是说数据包的往返时间越长,通信的效率就越低.为此引入了滑动窗口机制

窗口大小:无需等待确认应答,可以发送数据的最大值.窗口大小一般是由接收方的窗口大小来确定的.TCP头部有[窗口大小]的字段,这个字段就是接收方根据自己缓存的剩余空间来确认一个合适的滑动窗口的值发送给发送方.这样就就可以避免发送方发送数据速度过快而导致接收端处理不过来.

窗口的实现实际上是操作系统开辟的一块缓存空间,缓存空间中存储的是请求报文,请求报文在接收到确认应答后会从缓存中清除

发送方的滑动窗口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SgbU2v6G-1661958443459)(C:\Users\qiu\AppData\Roaming\Typora\typora-user-images\1659671251983.png)]

滑动窗口由4个部分组成

  1. 已发送并收到ACK确认的数据
  2. 已发送但未收到ACK确认的数据
  3. 未发送但大小在接收方处理范围内(可用窗口)
  4. 未发送且大小不再接收方处理范围内

注:2,3共同构成发送窗口:2是正在使用的,3是未使用的.

当有数据接收到ACK确认时,滑动窗口会发生偏移,也就可以继续发送新的数据.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-by8N7dVK-1661958443459)(C:\Users\qiu\AppData\Roaming\Typora\typora-user-images\1659671415051.png)]

利用三个指针来跟踪四个传输类别中每一个类别中的字节.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kcDZqUbw-1661958443459)(C:\Users\qiu\AppData\Roaming\Typora\typora-user-images\1659671645331.png)]

  1. SND.UNA:指向发送窗口的第一个字节
  2. SND.NXT:指向可用窗口的第一个字节
  3. SND.WND:描述发送窗口的大小.指向#4的第一个字节的位置可以通过SND.UNA+SND.WND得到.

接收方的滑动窗口

接收方的滑动窗口由3部分组成

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pzg6s9A0-1661958443459)(C:\Users\qiu\AppData\Roaming\Typora\typora-user-images\1659671788913.png)]

  1. 已成功接收并确认的数据
  2. 未收到数据但可以接收的数据
  3. 未收到数据且不可以接收的数据

相应的利用2个指针来划分

  1. RCV.WND:描述接收窗口的大小
  2. RCV.NXT:指向接收窗口的第一个字节

接收窗口和发送窗口的大小关系是约等于.因为发送窗口的大小需要接收端根据自己接收窗口的大小决定.因此两者的大小关系存在一定的时延(即接收窗口大小先更新,发送窗口大小后更新)

流量控制

发送方不能无脑的发送数据给接收方,要考虑接收方数据处理能力.

如果无脑发送数据的速率超过了接收方处理的速率,会触发发送方的重发机制导致资源的浪费.为此引入了流量控制的机制:发送方会根据接收方的实际接受能力控制发送的数据量

流量控制的具体流程:

  1. 接收端会将自己的接收缓冲区的大小放入TCP首部的"窗口"字段,通过ACK反馈给发送端. 这个"窗口"也就是TCP首部的"16位窗口大小",当然这个缓冲区的大小不止65535位.在TCP首部40字节选项中还包含了一个窗口扩大因子M,实际缓冲区大小是窗口字段的值左移M位.
  2. 当接收端发现自己的缓冲区中的数据快满了,就会减小窗口的值然后反馈给发送端,发送端依据此放慢自己的发送速度
  3. 当接收端发现自己的缓冲区已经满了,就会将窗口的值设置为0,发送端根据这个值变为0后将不再发送数据,但会定期的发送一个窗口探测的数据段用来得到接收区中缓冲区的剩余大小.当接收端中缓冲区大小有剩余时,会向发送端发送一个窗口更新通知.

操作系统缓冲区与滑动窗口的关系

发送方和接收方的滑动窗口中存放的字节数实际上都是放在操作系统内存缓冲区的.

当应用进程没办法即使读取缓冲区中的内容时,会对缓冲区造成影响,进而会对窗口造成影响.

当服务器资源十分紧张时会缩小接收缓冲区的大小.因此当客户端发送数据包给服务器时,服务器由于资源紧张减少接收缓冲区的大小,并且应用层无法读取数据,进一步减少了接收窗口的可用大小.也就是说此刻发送窗口的大小是大于接收窗口的(因为接收窗口因为资源紧张减少了大小),在接收方返回最新窗口之前,假设发送了一个超过此时接收方窗口大小的数据,则会造成服务器丢包,同时客户端中的发送窗口的大小会变成负值.

因此,为了防止这种情况发生,TCP规定不允许同时减少缓存且收缩窗口.而是先收缩窗口,之后再减少缓存,避免丢包现象的发生.

窗口关闭

当接收方的窗口大小变为0时,意味着窗口关闭,告诉发送方在这一段时间内不要再发送数据.当接收方数据处理了一部分以后,会主动发送一个ACK报文给发送方告知发送方可以发送数据了.

这里存在一个危险:当接收端窗口大小从0变为非0后,传递给发送方的ACK报文如果发生丢包,发送方和接收方就会处于一种死等的状态.[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mqKqTGQ8-1661958443460)(C:\Users\qiu\AppData\Roaming\Typora\typora-user-images\1659702573200.png)]

为了解决接收窗口为0后发送的ACK报文的丢包造成的死锁问题,TCP为每一个连接设定一个持续定时器,这个持续定时器从发送方接收到接收方发送的窗口大小为0的通知开始计时.当计时器超时,发送方就会发送一个窗口探测报文,而对方在收到窗口探测报文后会给出自己当前窗口的大小:如果为0,则重启定时器;如果不为0,则打破死锁.窗口探测报文在连续探测3次后收到的窗口大小认为0,TCP一般就会发送RST报文来中断连接

糊涂窗口综合症

如果接收方太忙了,来不及取走接收窗口里的数据,那么发送方的发送窗口会越来越小.到最后,窗口中只剩下很小的可用区域,然后每次还得发送接收窗口的大小给发送方,然后发送方还会顽固地发送很少字节数的数据,这就是糊涂窗口综合症.

用TCP+IP头部一共40个字节的报文去发送数据只有几个字节的数据会耗费太多不必要的资源

要解决糊涂窗口综合症,就需要避免接收方将小窗口发送给发送方以及避免发送方发送小的数据给接收方.

  1. 避免接收方放松小窗口给发送方

    接收方发送窗口的策略:当窗口大小小于min(MSS,缓存空间/2)时,会通告一个窗口大小为0的数据报文给发送方.当接收方的可用窗口大小大于min(MSS,缓存空间/2)后把窗口的真实大小返回给发送方.

  2. 避免发送方发送小的数据给接收方

    发送方发送的策略:使用Nagle算法延时处理,当不满足下述任意一个条件时则不发送数据

    1. 要等到窗口大小>=MSS或发送数据大小>=MSS
    2. 收到之前发送数据的ack包

    由此可知Nagle算法是依靠避免接收方发送小窗口给发送方

    所以避免糊涂窗口综合症的方案是:不通告小窗口给对方+Nagle算法

    Nagle算法默认是打开的,但对于某些特殊的场景需要发送小数据给对方时,则需要手动关闭Nagle算法(设置TCP_NODELAY)

拥塞控制

流量控制可以保证发送方发送的数据填满接收方的缓存,但并不能对网络拥堵等情况作出相应的反应.当网络拥堵时,发送的数据包丢包,时延的概率就更大.因此发送方就会重传数据,重传的数据会加剧网络拥堵,然后会更容易丢包,时延.因此会造成一种恶性循环. 因此引出了拥塞控制:当网络发生拥堵时要减少发送的数据量

为了在发送方动态调节要发送的数据量,需要依靠一个拥塞窗口.

拥塞窗口是发送方维护的一个状态量,根据网络拥塞程度动态变化.因此滑动窗口的大小就等于min(拥塞窗口,接收窗口)

当网络中没有拥塞时,拥塞窗口会增大;当网络中出现拥塞时,拥塞窗口会减小**.网络是否拥塞通过发送方是否能在规定时间内接收到ACK报文来衡量.**

拥塞控制由4个算法构成

  1. 慢启动
  2. 拥塞避免算法
  3. 拥塞发生
  4. 快速恢复

慢启动

TCP在刚建立连接完成后,会有一个慢启动的过程.这个慢启动就是一点一点增加发送包的数量.

慢启动的规则:当发送方每接收到1个ACK,拥塞窗口的大小就会+1.(这个1指的是1个MSS大小的数据包)

因此,拥塞窗口的大小随着接收ACK数量的增多,1,2,4,8…呈指数趋势增长.慢启动过程有一个慢启动门限ssthresh,当拥塞窗口大小小于ssthresh时,采用慢启动算法,当拥塞窗口大小大于等于ssthresh时,采用拥塞避免算法.一般来说ssthresh的大小为66535字节.

拥塞避免算法

当拥塞窗口大小大于等于ssthresh时,会采用拥塞避免算法.

拥塞避免算法的规则:当发送方没接收到1个ACK,拥塞窗口的大小就会增加(1/拥塞窗口大小)

我们从慢启动的指数增长中退出到拥塞避免中,在拥塞避免中,拥塞窗口的大小会呈线性增长.

之后当拥塞窗口的大小增长到一定程度后,也会发生网络堵塞.此时就会触发发送方的重传机制,因此也就进入了拥塞发生

拥塞发生

当拥塞窗口的大小增长到发生网络堵塞触发发送方的重传机制时采用拥塞发生算法.

拥塞发生算法的使用取决于采用哪种重传机制

  1. 超时重传

    使用拥塞发生算法:ssthresh的值变为拥塞窗口大小的一般,拥塞窗口的大小变为拥塞窗口的初始值.之后就继续慢启动.

    这种拥塞发生算法过于激进,相当于一个断崖式下降,会造成网络卡顿

  2. 快速重传

    当触发快速重传机制时,TCP会认为这种情况丢包现象不严重,因此拥塞窗口变为原来的一半,ssthresh变为新的拥塞窗口大小,之后进入快速恢复算法.

快速恢复

快速恢复是在快速重传的前提下才发生的.快速恢复算法认为既然能够触发快速重传接收到3个重复的ACK包,说明网络拥塞情况不严重,不需要向超时重传的拥塞发生算法那么激烈.

快速恢复算法的流程

  1. 拥塞窗口大小加3(3指的是快速重传中接收到的3个相同的ACK包)
  2. 重传丢失的数据包.
  3. 如果收到重复的ACK包拥塞窗口的大小+1(这个过程是尽可能重传丢失的数据包)
  4. 当收到新的ACK包时,说明之前丢失的包已经全部发送完.至此快速恢复算法结束.拥塞窗口大小变为ssthresh的值,然后进入拥塞避免算法.

快速恢复算法相当于是对慢启动算法的优化.进入快速优化算法说明当前拥塞窗口的大小较大,会引发网络拥堵,所以要减小拥塞窗口的大小,但网络拥堵情况又没有太严重所以不需要像超时重传的拥塞发生算法那样下降的那么剧烈.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-umOLVcTd-1661958443460)(C:\Users\qiu\AppData\Roaming\Typora\typora-user-images\1659749665684.png)]

上图是触发超时重传的拥塞控制过程

下图是触发快速重传的拥塞控制过程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zCanflWF-1661958443460)(C:\Users\qiu\AppData\Roaming\Typora\typora-user-images\1659749688985.png)]

延迟确认

在糊涂窗口综合症的解决方案中提到了Nagle算法,Nagle算法+接收方避免通告小窗口可以解决糊涂窗口综合症,

但即使开启Nagle算法,也可能会在最开始的时候发送较小数据的报文.

延迟确认则是为了解决ACK传输效率低的问题.即ACK报文中的数据很少甚至没有仍需要发送的问题

延迟确认的策略:

  1. 当有响应数据发送时,ACK会随着响应数据一起立刻发送给对方.
  2. 当没有响应数据要发送时,ACK会延迟一段时间,以等待是否可以搭载响应报文的顺风车一起发送
  3. 如果在延迟等待发送期间,对方的第二个数据报文已经到达了,则立刻发送ACK报文.

当同时使用延迟确认和Nagle算法时会产生新的问题:首先发送方发送一个小数据给接收方,接收方因为没有要发送的数据,根据延迟确认策略会进行等待,而发送方因为Nagle算法要接收到对方的ACK报文才可以继续发送数据.这样就造成了发送方和接收方中在延迟确认等待的这一段时间内毫无动作,降低了传输效率.

解决方案就是关闭延迟确认和Nagle算法中的一个.

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

囚蕤

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

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

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

打赏作者

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

抵扣说明:

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

余额充值