TCP 可靠传输的实现(二)TCP的重传机制

TCP 可靠传输的实现(二)TCP的重传机制,第1张

TCP使用可靠的传输协议,即意味着必须按序、无差错的传送数据到目的端,那么如果在传输过程中发送的包丢失了该怎么办?TCP的重传机制就是:如果发送方认为发生了丢包现象就重发这些数据包。显然,我们需要一个方法去 猜测是否发生了丢包 。最简单的想法就是,接收方每接收到一个包就向发送者返回一个ACK,表示自己已经收到了这段数据,反过来,如果发送方一段时间内没有收到ACK,就知道 很可能是数据包丢失 了,紧接着就重发该数据包,直到收到ACK为止。

为什么是 猜测 呢? 因为即使是超时了,这个数据包也可能并没有丢,它只是绕了段远程,来的很晚而已。毕竟TCP协议是位于传输层的协议,不可能明确知道数据链路层和物理层发生了什么。但是这并不妨碍我们的超时重传机制,因为接收方会自动忽略收到的重复的包。

下面我们具体讲一讲TCP的重传机制:

这种机制下,每个数据包都有相应的计时器,当超过指定的时间后,没有收到对方的 ACK 确认应答报文就会重发该数据包。

超时时间应该设置为多少

我们先来了解一下 RTT (Round-Trip Time 往返时延)

而超时时间是以 RTO(Retransmission Timeout 超时重传时间) 表示。

超时时间不宜设置的过长或过短,否则:

综上可知,RTO设置的值应该略大于RTT的值。

RTO值的计算:

https://blog.csdn.net/JXH_123/article/details/27345151

值得注意的是:每触发一次超时重传,都 会将下一次超时时间间隔设为先前值的两倍 。遇到超时说明网络环境差,不宜频繁发送。

Wireshark 抓包显示:

超时重传存在的问题是:

 当一个报文段丢失时,会等待一定的超时时间后才重传,增加了端到端的时延;

 当一个报文段丢失时,在其等待超时的过程中,可能会出现这种情况: 其后的报文段已经被接收端接收但却迟迟得不到确认,发送端就也以为丢失了,从而引起不必要的重传,既浪费时间也浪费资源。(例如: 数据包5丢失,数据包6、7、8、9都已到达接收方,这个时候客户端只能等服务端发送ACK,因此对于客户端来说,它完全不知道丢了几个包,可能就悲观的认为:5后面的数据包都丢了,就重传这5个数据包,这就比较浪费了)。

刚刚提到过,基于计时器的重传往往要等待很长时间,而快速重传使用了很巧妙的方法来解决这个问题。

快速重传(Fast Retransmit)机制 不以时间为驱动,而是以数据为驱动重传。

由于TCP采用的是累计确认机制,当接收端收到比期望序号大的报文段时,便会重复发送最近一次确认的报文段的确认号,即 冗余 ACK (Duplicate ACK)。

这样,如果在超时重传定时器溢出之前,接收到连续的三个重复冗余 ACK (第一个ACK是正常的,后三个是冗余的),发送端便知晓哪个报文段在传输过程中丢失了,于是重发该报文段,而不需要等待超时重传定时器溢出,大大提高了效率。

Wireshark 抓包显示:

但是,快速重传仍然没有解决第二个问题:到底该重传多少个包?

改进的方法就是 SACK (Selective Acknowledgment),简单来说就是在快速重传的基础上,返回最近收到的报文段的序列号范围,这样客户端就知道,哪些数据包已经到达服务器了。

看下例子:

存在 SACK 选项时

当500-599报文到达,接收方发送  ACK 200  ,SACK [500,600)

当600-699报文到达,接收方发送  ACK 200  ,SACK [500,700)

当700-799报文到达

当800-899报文到达

当900-999报文到达,接收方累积确认发送  ACK 200  ,SACK [500,1000)

连续收到3个重复ACK,发送方经检查发现200-499的数据丢失了,执行快速重传,待接收方接收到200-499的数据,并返回 ACK 1000时,发送方的所有数据均已确认完毕,移动滑动窗口到1000位置处。

使用 SACK可以告知发送方 收到了哪些数据,发送方收到这些消息后就会知道哪些数据丢失,然后立即重传丢失的部分。

需要注意的是: 只有收到失序的分组时才可能会发送SACK 。

SACK 包括了两个TCP选项,一个选项用于标识是否支持 SACK(SACK_Permitted),在TCP建立连接时发送;另一种选项则包含了具体的 SACK信息。

(1)SACK_Permitted 选项

该选项只允许在TCP连接建立时,有 SYN标志的包中设置,在连接建立阶段,主动发起连接的一方在它的SYN中指定选项。只有在它从另一方的SYN中收到了这个选项之后,SACK机制才会被使能。

(2)SACK 信息选项

SACK 选项参数告诉对方 已经接收到 并缓存的不连续的数据块,发送方可据此信息检查究竟是哪个块丢失,从而发送相应的数据块。

 Left Edge:本区块的第一个序号。 Right Edge:本区块的最后序号的下一个序号。

[Left Edge, Right Edge)区间的ACK 序号表示本次确认收到的序号。

问题1:SACK选项最多能包含多少个需重传的块?

       由于TCP首部的最大长度为 60 byte,而固定首部占用了 20 byte,对于SACK选项本身占用了2 byte,所以剩下 60-20-2=38 byte。而每个块(包括开始和结束)占用 8 byte,所以最多可标识的块数为 38/8 = 4块,所以 SACK 最多可以包括4个需重传的块。同时由于SACK有些时候会和时间戳(占10字节)一起用,因此,此种情况下最多只有3个SACK。

问题2:SACK选项的使用规则是怎么样的?

SACK 的发送方,即 报文的接收端

第一个块需要指出是哪一个到达的报文触发的 SACK

尽可能多的把所有的块填满

SACK 要报告最近接收的不连续的数据块

SACK 的接收端,即 报文的发送端:

数据没有被确认前,都会保持在滑动窗口内

每个数据包都有一个 SACKed 的标志,对于已经标示的报文,再次接收到时会忽略

 如果SACK丢失,超时重传之后,重置所有数据包SACKed 标志

DSACK是在SACK的基础上做了一些 扩展 ,主要用于对收到的 重复报文 进行了处理。

它的主要作用是:告诉发送方有哪些数据被重复接收了。

DSACK同样使用了与SACK一样的报文格式,唯一区别在于: 第一个连续的block指定的是触发DSACK的重复报文的序号空间。如果第一个段的范围被ACK范围所覆盖,那么就是DSACK。或者,第一个段的范围被SACK的第二个段覆盖,那么就是DSACK。

引入DSACK的好处有:

1)可以让发送方知道,是发出去的包丢了,还是回来的ACK包丢了;

2)是不是自己的 timeout 设置太小了,导致重传;

3)网络上出现了先发的包后到的情况(又称数据包失序);

4)网络上是不是把我的数据包给复制了;

总之,DSACK的目的是帮助发送方判断,是否发生了包失序、ACK丢失、包重复或伪重传,让TCP可以更好的做网络流量控制。

超时重传机制能解决数据包丢失的问题,但是超时重传机制存在等待时间太长,浪费时间在等待上,降低了传输效率和无法知道需要重传哪些数据包的问题。 快速重传能解决超时重传的等待时间太长的问题,但是对于究竟该重传哪些包的问题仍然不能有效解决。SACK能需要重传哪些数据包的问题,它可以知道哪些包是被确认接收的,客户端能据此判断需要重传的包。DSACK则是作为SACK的一个辅助措施,可以用来判断网络究竟是出现了什么情况,据此做好网络流量控制。

- 在网络层面,传输都可能存在丢包和不可靠。tcp之所以可靠,是因为他有重传机制。但即使如此,也不能说百分之百就一定能送达,也就是当重传还是丢了后,tcp也表示无能为力,几番尝试无果就只能断链接了。当然这时你可以选择重链接,但谁也不能保证重连就一定能连上。所以,一切还是看具体怎么实现,怎么考虑各种极端的网络情况。网络特别特别好,udp传输也不怎么丢包的。

- 同意,TCP可靠是基于双方都知道彼此的字节流的原点,发送方对于自己发送的字节流一定要接收到对方的确认,才会从发送缓冲区彻底删除,如果没有得到确认,会一直重传,如果重传次数到达极限值,则reset TCP 连接。而UDP则不同,把数据发出去就不管了,也没有缓存区来缓存发出去而没有确认的数据,因为压根不会发生,此外也无需内存来保存类似TCP各种状态位。

简单回答的话,在TCP的实现方面,超时重传 快速重传和SACK机制都能在这种情况下实现让客户端重新传输丢掉的第2个包,分别讲讲三种不同的机制

1. 超时重传

客户端通过tcp一次性把数据都发送了出去,在未收到这些发出报文的ack确认之间,这些报文仍然会缓存在发送队列里,如果在限定的时间内(RTO 重传过期时间)没有收到对应报文的ack,那么这些报文需要被重传

RTO不是静态配置的,在这个问题里系统会根据前面三次握手和第一个报文的ack的RTT计算出来一个RTO,这个时间可能是200ms到120s之间的一个值,所有到了RTO时间没有收到ack的报文都需要被重新传送到网络上

2. 快速重传

等待一个RTO的时间太久了,尤其是在RTT比较大的网络环境里,如果丢包率再比较高的情况,那么TCP的效率就会非常低,所以需要通过快速重传机制来实现优化

简单来说,客户端发送的第2个报文丢了,没有收到ack,但服务器在收到第3 4 5个包时会检查他们的顺序编号sequence number,并察觉到与第1个包之间并不连续,于是会发送3个连续的dup ack 给客户端,要求客户端重新发送第1个包后面具有正确sequence number的报文 客户端在收到三个连续的dup ack之后就会确认第2个报文丢掉了,于是实现立即重传而不用再等到RTO的过期时间

3. SACK selective acknowledgment

如果只是丢了第2个包其实快速重传机制就已经可以搞定了,但如果在中间丢了多个报文的时候,比如2-9之间可能丢了第3,第5和第7个报文,其他报文都已经顺利到达服务器,如果只有快速重传,那么客户端需要重传从3到第7个之间的所有报文,而如果客户端和服务器都支持sack的话,服务器端就可以通过sack选项来告知发送方到底丢了哪些报文,这样客户端就只需要把对应丢失的报文重传就好了。二、TCP重传

1、重传的原因

1)发端计时器超时

TCP每发送一个报文段,就对这个报文段设置一次计时器。当计时器超时而没有收到确认时,就重传该报文。

注:原来报文哪去了呢?两种可能:1)在中间节点丢了。2)还在路上,走的慢。

对于第一种情况:接收端是不知情的,而对于第二种情况,接收端表现为收到两个一摸一样的报文。

呵呵,主机A仅仅重传3

为什么呢,原因很简单,TCP的通信是以数据包为单位的

1.一个数据包发送之后,只有收到对方的ACK包,才会认为该数据包被正确接收了

2.如果超时后还没有收到ACK那么就进行重传,并且只对该数据包重传,因为

它已经收到了其它数据包的ACK,所以无需对其它数据包进行重传!!!!

3.还有一种情况,如果数据包在传输过程中损坏了,那么接收方会产生一个NCK报文,发送给发送方,发送方也会进行重传,并且仅仅重传这一个数据包。


欢迎分享,转载请注明来源:夏雨云

原文地址:https://www.xiayuyun.com/zonghe/758581.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-08-18
下一篇2023-08-18

发表评论

登录后才能评论

评论列表(0条)

    保存