罗岗网站建设价格,上海建设厅网站首页,接工程平台,响应式布局基本知识重传机制 TCP 针对数据包丢失的情况#xff0c;会用重传机制解决。
接下来说说常见的重传机制#xff1a;
超时重传快速重传SACKD-SACK 超时重传 重传机制的其中一个方式#xff0c;就是在发送数据时#xff0c;设定一个定时器#xff0c;当超过指定的时间后#xff0c… 重传机制 TCP 针对数据包丢失的情况会用重传机制解决。
接下来说说常见的重传机制
超时重传快速重传SACKD-SACK 超时重传 重传机制的其中一个方式就是在发送数据时设定一个定时器当超过指定的时间后没有收到对方的 ACK 确认应答报文就会重发该数据也就是我们常说的超时重传。
TCP 会在以下两种情况发生超时重传
数据包丢失确认应答丢失 超时时间应该设置为多少呢 我们先来了解一下什么是 RTTRound-Trip Time 往返时延从下图我们就可以知道 RTT 指的是数据发送时刻到接收到确认的时刻的差值也就是包的往返时间。
超时重传时间是以 RTO Retransmission Timeout 超时重传时间表示。
假设在重传的情况下超时时间 RTO 「较长或较短」时会发生什么事情呢 上图中有两种超时时间不同的情况
当超时时间 RTO 较大时重发就慢丢了老半天才重发没有效率性能差当超时时间 RTO 较小时会导致可能并没有丢就重发于是重发的就快会增加网络拥塞导致更多的超时更多的超时导致更多的重发。
精确的测量超时时间 RTO 的值是非常重要的这可让我们的重传机制更高效。
根据上述的两种情况我们可以得知超时重传时间 RTO 的值应该略大于报文往返 RTT 的值。 在发送端发包时记下 t0 然后接收端再把这个 ack 回来时再记一个 t1于是 RTT t1 – t0这样对吗没这么简单。
实际上「报文往返 RTT 的值」是经常变化的因为我们的网络也是时常变化的。也就因为「报文往返 RTT 的值」 是经常波动变化的所以「超时重传时间 RTO 的值」应该是一个动态变化的值。
我们来看看 Linux 是如何计算 RTO 的呢
估计往返时间通常需要采样以下两个
需要 TCP 通过采样 RTT 的时间然后进行加权平均算出一个平滑 RTT 的值而且这个值还是要不断变化的因为网络状况不断地变化。除了采样 RTT还要采样 RTT 的波动范围这样就避免如果 RTT 有一个大的波动的话很难被发现的情况。
如果超时重发的数据再次超时的时候又需要重传的时候TCP 的策略是超时间隔加倍。
也就是每当遇到一次超时重传的时候都会将下一次超时时间间隔设为先前值的两倍。两次超时就说明网络环境差不宜频繁反复发送。
超时触发重传存在的问题是超时周期可能相对较长。那是不是可以有更快的方式呢
于是就可以用「快速重传」机制来解决超时重发的时间等待。 快速重传 TCP 还有另外一种快速重传Fast Retransmit机制它不以时间为驱动而是以数据驱动重传。
快速重传机制是如何工作的呢其实很简单一图胜千言。 在上图发送方发出了 12345 份数据
第一份 Seq1 先送到了于是就 Ack 回 2结果 Seq2 因为某些原因没收到Seq3 到达了于是还是 Ack 回 2后面的 Seq4 和 Seq5 都到了但还是 Ack 回 2因为 Seq2 还是没有收到发送端收到了三个 Ack 2 的确认知道了 Seq2 还没有收到就会在定时器过期之前重传丢失的 Seq2。最后收到了 Seq2此时因为 Seq3Seq4Seq5 都收到了于是 Ack 回 6 。
所以快速重传的工作方式是当收到三个相同的 ACK 报文时会在定时器过期之前重传丢失的报文段。
快速重传机制只解决了一个问题就是超时时间的问题但是它依然面临着另外一个问题。就是重传的时候是重传一个还是重传所有的问题。
举个例子假设发送方发了 6 个数据编号的顺序是 Seq1 ~ Seq6 但是 Seq2、Seq3 都丢失了那么接收方在收到 Seq4、Seq5、Seq6 时都是回复 ACK2 给发送方但是发送方并不清楚这连续的 ACK2 是接收方收到哪个报文而回复的 那是选择重传 Seq2 一个报文还是重传 Seq2 之后已发送的所有报文呢Seq2、Seq3、 Seq4、Seq5、 Seq6 呢 如果只选择重传 Seq2 一个报文那么重传的效率很低。因为对于丢失的 Seq3 报文还得在后续收到三个重复的 ACK3 才能触发重传。 如果选择重传 Seq2 之后已发送的所有报文虽然能同时重传已丢失的 Seq2 和 Seq3 报文但是 Seq4、Seq5、Seq6 的报文是已经被接收过了对于重传 Seq4 Seq6 折部分数据相当于做了一次无用功浪费资源。
可以看到不管是重传一个报文还是重传已发送的报文都存在问题。
为了解决不知道该重传哪些 TCP 报文于是就有 SACK 方法。 SACK 方法 还有一种实现重传机制的方式叫SACK Selective Acknowledgment 选择性确认。
这种方式需要在 TCP 头部「选项」字段里加一个 SACK 的东西它可以将已收到的数据的信息发送给「发送方」这样发送方就可以知道哪些数据收到了哪些数据没收到知道了这些信息就可以只重传丢失的数据。
如下图发送方收到了三次同样的 ACK 确认报文于是就会触发快速重发机制通过 SACK 信息发现只有 200~299 这段数据丢失则重发时就只选择了这个 TCP 段进行重复。 如果要支持 SACK必须双方都要支持。在 Linux 下可以通过 net.ipv4.tcp_sack 参数打开这个功能Linux 2.4 后默认打开。 Duplicate SACK Duplicate SACK 又称D-SACK其主要使用了 SACK 来告诉「发送方」有哪些数据被重复接收了。
下面举例两个例子来说明 D-SACK 的作用。
例一ACK 丢包 「接收方」发给「发送方」的两个 ACK 确认应答都丢失了所以发送方超时后重传第一个数据包3000 ~ 3499于是「接收方」发现数据是重复收到的于是回了一个 SACK 3000~3500告诉「发送方」 3000~3500 的数据早已被接收了因为 ACK 都到了 4000 了已经意味着 4000 之前的所有数据都已收到所以这个 SACK 就代表着 D-SACK。这样「发送方」就知道了数据没有丢是「接收方」的 ACK 确认报文丢了。
例二网络延时 数据包1000~1499 被网络延迟了导致「发送方」没有收到 Ack 1500 的确认报文。而后面报文到达的三个相同的 ACK 确认报文就触发了快速重传机制但是在重传后被延迟的数据包1000~1499又到了「接收方」所以「接收方」回了一个 SACK1000~1500因为 ACK 已经到了 3000所以这个 SACK 是 D-SACK表示收到了重复的包。这样发送方就知道快速重传触发的原因不是发出去的包丢了也不是因为回应的 ACK 包丢了而是因为网络延迟了。
可见D-SACK 有这么几个好处
可以让「发送方」知道是发出去的包丢了还是接收方回应的 ACK 包丢了;可以知道是不是「发送方」的数据包被网络延迟了;可以知道网络中是不是把「发送方」的数据包给复制了;
在 Linux 下可以通过 net.ipv4.tcp_dsack 参数开启/关闭这个功能Linux 2.4 后默认打开。 滑动窗口 引入窗口概念的原因 我们都知道 TCP 是每发送一个数据都要进行一次确认应答。当上一个数据包收到了应答了 再发送下一个。
这个模式就有点像我和你面对面聊天你一句我一句。但这种方式的缺点是效率比较低的。
如果你说完一句话我在处理其他事情没有及时回复你那你不是要干等着我做完其他事情后我回复你你才能说下一句话很显然这不现实。 所以这样的传输方式有一个缺点数据包的往返时间越长通信的效率就越低。
为解决这个问题TCP 引入了窗口这个概念。即使在往返时间较长的情况下它也不会降低网络通信的效率。
那么有了窗口就可以指定窗口大小窗口大小就是指无需等待确认应答而可以继续发送数据的最大值。
窗口的实现实际上是操作系统开辟的一个缓存空间发送方主机在等到确认应答返回之前必须在缓冲区中保留已发送的数据。如果按期收到确认应答此时数据就可以从缓存区清除。
假设窗口大小为 3 个 TCP 段那么发送方就可以「连续发送」 3 个 TCP 段并且中途若有 ACK 丢失可以通过「下一个确认应答进行确认」。如下图 图中的 ACK 600 确认应答报文丢失也没关系因为可以通过下一个确认应答进行确认只要发送方收到了 ACK 700 确认应答就意味着 700 之前的所有数据「接收方」都收到了。这个模式就叫累计确认或者累计应答。 窗口大小由哪一方决定 TCP 头里有一个字段叫 Window也就是窗口大小。
这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据而不会导致接收端处理不过来。
所以通常窗口的大小是由接收方的窗口大小来决定的。
发送方发送的数据大小不能超过接收方的窗口大小否则接收方就无法正常接收到数据。 发送方的滑动窗口 #1 是已发送并收到 ACK确认的数据1~31 字节#2 是已发送但未收到 ACK确认的数据32~45 字节#3 是未发送但总大小在接收方处理范围内接收方还有空间46~51字节#4 是未发送但总大小超过接收方处理范围接收方没有空间52字节以后
在下图当发送方把数据「全部」都一下发送出去后可用窗口的大小就为 0 了表明可用窗口耗尽在没收到 ACK 确认之前是无法继续发送数据了。 在下图当收到之前发送的数据 32~36 字节的 ACK 确认应答后如果发送窗口的大小没有变化则滑动窗口往右边移动 5 个字节因为有 5 个字节的数据被应答确认接下来 52~56 字节又变成了可用窗口那么后续也就可以发送 52~56 这 5 个字节的数据了。 程序是如何表示发送方的四个部分的呢 TCP 滑动窗口方案使用三个指针来跟踪在四个传输类别中的每一个类别中的字节。其中两个指针是绝对指针指特定的序列号一个是相对指针需要做偏移。 SND.WND表示发送窗口的大小大小是由接收方指定的 SND.UNASend Unacknoleged是一个绝对指针它指向的是已发送但未收到确认的第一个字节的序列号也就是 #2 的第一个字节。 SND.NXT也是一个绝对指针它指向未发送但可发送范围的第一个字节的序列号也就是 #3 的第一个字节。 指向 #4 的第一个字节是个相对指针它需要 SND.UNA 指针加上 SND.WND 大小的偏移量就可以指向 #4 的第一个字节了。
那么可用窗口大小的计算就可以是
可用窗口大小 SND.WND -SND.NXT - SND.UNA