滑动窗口协议(Sliding Window Protocol),属于
TCP协议的一种应用,用于网络数据传输时的流量控制,以免拥塞的发生。该协议容许发送方在中止并等待确认前发送多个数据分组。因为发送方没必要每发一个分组就停下来等待确认,所以该协议能够加速数据的传输,提升网络吞吐量。
-
中文名
-
滑动窗口协议
-
外文名
-
Sliding Window Protocol
-
类 型
-
可靠数据传输协议
-
层次结构
-
4层
若是过多的源同时以很快的速度发送大量的数据包,而此时接收方并无如此高的接收数据的能力,所以极易致使网络的拥塞。因此,为了控制发送方的发送速度,防止发送方并考虑到受发送缓冲区大小的制约等,要求对发送方已发出但还没有经确认的帧的数目加以限制,同时使网络的传输效率获得提升,滑动窗口协议应运而生,它使得发送方能够在未收到确认的状况下,同时发送多个数据分组,由此大大提高了网络吞吐量。
在任何基于自动重发请求进行错误控制的通讯协议中,接收方必须确认收到的数据包。 若是发送方在合理的时间内没有收到确认,则重发数据。没有听到确认的发送方不知道接收方是否实际接收到分组(数据可能在传输中丢失或损坏)。 若是错误检测显示损坏,则数据包将被接收方忽略,而且不会发送确认。 由于网络传输的时延,将有大量时间被用于等待确认,致使传输效率低下。
传输的每一个部分被分配惟一的连续序列号,接收方使用数字并以正确的顺序放置接收到的数据包,丢弃重复的数据包并识别丢失的数据。
协议中规定,对于窗口内未经确认的分组须要重传。这种分组的数量最多能够等于发送窗口的大小,即滑动窗口的大小n减去1(由于发送窗口不可能大于(n-1),起码接收窗口要大于等于1)。
滑动窗口协议必须保证数据包的按序传输,发送窗口中的序列号表明已发送但还没有收到确认的数据包,发送窗口可持续地维持一系列未经确认的数据包,由于发送方窗口内的数据包可能在传输过程当中丢失或损坏,因此发送过程必须把发送窗口中的全部数据包保存起来以备重传。发送窗口一旦达到最大值,发送过程就必须中止接收新的数据包,直到有空闲缓存区。接收窗口外的数据包都要丢弃,当序列号等于接收窗口下限的数据包到达时,把它提交给应用程序并向发送端发送确认,接收窗口向前移动一位。发送窗口和接收窗口上下限无需相同,大小也无需相同,但接收窗口大小需保持固定,发送窗口大小可随着数据包而改变。
[1]
滑动窗口协议的时间线
工做原理
经过限制在任何给定时间能够发送或接收的数据包的数量,滑动窗口协议容许使用固定大小的序列号传送无限数量的数据包。发送方侧的术语“窗口”表示接收方还没有确认的分组总数的逻辑边界。接收方在每一个确认包中通知发送器当前的最大接收缓冲区大小(窗口边界)。 TCP报头使用16位字段向发送方报告接收窗口大小。所以,可使用的最大窗口是2^16 = 64千字节。在慢启动模式下,发送器以低分组计数器开始,而且在从接收方接收到确认分组以后增长每一个传输中的分组数量。对于接收的每一个ACK分组,该窗口经过一个分组(逻辑地)滑动以传送一个新分组。当达到窗口阈值时,发送器发送一个包,用于接收到的一个ACK分组(确认分组)。若是窗口限制为10个数据包,则在慢启动模式下,发送器能够开始发送一个数据包,后跟两个数据包(发送两个数据包以前必须接收一个数据包),其次是三个数据包等等,直到10个数据包。可是在达到10个分组以后,进一步的传输被限制为一个接收到的一个分组发送的分组。在仿真中,看起来好像窗口对于接收到的每一个ACK分组移动一个分组距离。在接收方侧,窗口也会为接收到的每一个数据包移动一个数据包。滑动窗口方法能够确保网络上的交通拥堵得以免。应用层仍将提供传输到TCP的数据,而不用担忧网络流量拥塞问题,由于发送方和接收方的TCP实现分组缓冲区的滑动窗口。窗口大小可能根据网络流量而动态变化。
操做
发送方和接收方分别具备当前序列号nt和nr。它们各自还有一个窗口大小wt和wr。窗口大小可能会根据网络流量的变化而有所不一样,可是在更简单的实现中它们是固定的。窗口大小必须大于零才能进行任何操做。
一般状况下,nt是要发送到下一个分组,即还没有发送的第一分组的序列号。一样地,nr还没有收到的第一个分组的序列号。这两个序列号会随着时间逐渐增长。
接收方还能够跟踪未接收到的最高序列号,变量ns比接收到的最高序列号还多一。对于仅接受数据包(wr = 1)的简单接收方,这与nr相同,但若是wr> 1,则能够更大。注意区别:已经收到nr如下的全部数据包,没有接收到ns以上的任何数据包,在nr和ns之间,已经收到一些数据包。
当接收方接收到一个数据包时,它会适当地更新其变量,并用新的nr发送确认。发送方跟踪其收到的最高确认。发送方知道已经接收到但不包括na的全部分组,可是对于na和ns之间的分组是不肯定的,即na≤nr≤ns。
而且序列号老是符合na≤nr≤nx≤nt≤na+wt的规则,证实以下:
na≤nr:发送器接收到的最高确认不能高于接收方确认的最高nr。
nr≤ns:彻底接收的数据包的范围不能超出部分接收的数据包的结尾。
ns≤nt:接收到的最高报文不能高于发送的最高报文。
nt≤na + wt:发送的最高数据包同时受到接收到的最高确认和发送窗口大小的限制。
发送方操做
每当发送方具备要发送的数据时,它能够在最新的确认na以前传输序列号高达wt数据包。也就是说,只要nt<na + wt,它能够传送分组号nt。
在没有通讯错误的状况下,发送方很快就会收到全部发送的数据包的确认信息,这等于nt。若是在合理的延迟以后不会发生这种状况,则发送方必须在na和nt之间重传数据包。
接收方操做
每次接收到一个编号为x的数据包时,接收方检查它是否落入接收窗口,nr≤x<ns + wr。 (最简单的接收方只须要跟踪一个值nr =ns。)若是它落在窗口内,接收方接受它。若是编号为nr,则接收序列号增长1,而且若是先前接收和存储更多的连续分组,则可能更多。若是x> nr,则存储数据包直到接收到全部先前的数据包为止。若是x≥ns,后者更新为ns = x + 1。
若是数据包的序列号不在接收窗口内,则接收方将丢弃该数据包,而且不修改nr或ns。不管数据包是否被接受,接收方发送包含当前nr的确认。 (确认还能够包括关于nr或ns之间接收的附加数据包的信息,但这只能帮助效率。)
请注意,没有必要让接收窗口wr大于发送窗口wt,由于不须要担忧接收到永远不会发送的数据包;有用范围为1≤wr≤wt。
滑动窗口协议以基于分组的数据传输协议为特征。所以该协议适用于对按顺序传送分组的可靠性要求较高的环境,例如在数据链路层(OSI模型)以及传输控制协议(TCP)中。
[2]
加强应答的链路层重传,在长线传输中,因软故障形成的消息传输错误占据了绝大部分,对于这些问题的解决能够是系统的可靠性大大提升。这种机制,向经过实现简单、检突发错误能力高的CRC码的校验进行错误检查,再由相应的滑动窗口协议实现重传恢复。
[3]
[1] 中止等待协议(stop-and-wait)
中止等待协议示意图
这时接受方的窗口和发送方的窗口大小都是1,1个比特就够表示了,因此也叫1比特滑动窗口协议。发送方这时天然发送每次只能发送一个,而且必须等待这个数据包的ACK,才能发送下一个。虽然在效率上比较低,带宽利用率明显较低,不过在网络环境较差,或是带宽自己很低的状况下,仍是适用的。
存在的问题是,当发送方交替发送标记为“奇数”和“偶数”的数据包。 发送的确认一样为“奇数”和“偶数”。 假设已经发送了奇数分组的发送方没有收到奇数确认,而是当即发送下一个偶数分组,在此以后它可能会收到一个确认,为“下一个奇数包”。这将使发送方出现不肯定因素:接收方有可能接收到这两个数据包,或者二者都没接收到。
[2]回退n步协议(GO-BACK-N)
回退N-步协议示意图
因为中止等待协议效率过低,所以有了回退n-步协议,这也是滑动窗口协议真正的用处,这里发送的窗口大小为n,接受方的窗口仍然为1。具体看下面的图,这里假设n=9: 首先发送方一口气发送10个数据帧,前面两个帧正确返回了,数据帧2出现了错误,这时发送方被迫从新发送2-8这7个帧,接受方也必须丢弃以前接受的3-8这几个帧。 后退n协议的好处无疑是提升了效率,可是一旦网络状况糟糕,则会致使大量数据重发,反而不如上面的停等协议。
存在的问题在于,假设咱们使用3位序列号,这是
HDLC的典型值。 这使得N =
= 8。 因为wr = 1,咱们必须限制wt≤7。 这是由于在发送7个数据包以后,有8个可能的结果:0到7个数据包均可能被成功地接收。 这有8种可能性,发送方在确认中须要足够的信息来区分它们。若是发送方发送8个数据包而不等待确认,则可能会发现本身存在和中止等待协议同样的问题:这意味着全部8个数据包均可能被成功接收,亦或是一个都没有被成功接收。
[3]选择重传协议(selective repeat)
后退n协议的另一个问题是,当有错误帧出现后,老是要重发该帧以后的全部帧,毫无疑问在网络不是很好的状况下会进一步恶化网络情况。
重传协议即是用来解决这个问题。原理也很简单,接收端总会缓存全部收到的帧,当某个帧出现错误时,只会要求重传这一个帧,只有当某个序号后的全部帧都正确收到后,才会一块儿提交给高层应用。重传协议的缺点在于接受端须要更多的缓存。
存在的问题在于:最为广泛的HDLC协议使用3位序列号,并具备选择性重复的可选条件。可是,若是使用选择性重复,则必须保持nt +nr≤8的要求;若是wr增长到2,则wt必须下降到6。假设wr = 2,可是与wt = 7一块儿使用未修改的发射机。进一步假设接收器以nr = ns = 0开始。
如今假设接收器看到如下一系列数据包(均为模8):
0 1 2 3 4 5 6(暂停)0
因为wr = 2,接收方将接受并存储最终的数据包0(在系列中认为它是数据包8),同时请求重发数据包7。.然而,发送方也不可能接收到任何确认,而且在后一种状况下,接收机将接收错误的分组做为分组8。解决方案是发送方限制wt≤6。经过这种限制,接收方在接收到分组6后知道发送方的na≥1,而且所以编号为0的后续分组必须是分组8。若是全部确认丢失,则发送方将不得不在分组5以后中止。
(1)发送方没必要发送一个全窗口大小的数据。
(2)来自接收方的一个报文段确认数据并把窗口向右边滑动,这是由于窗口的大小是相对于确认序号的。
(3)窗口的大小能够减少,可是窗口的右边沿却不可以向左移动。
(4)接收方在发送一个ACK前没必要等待窗口被填满。
因为“滑动窗口”协议的性能取决于窗口大小和网络接收数据包的速度,在流量不稳定的环境中,性能降低甚至可能会使网络发生冲突。 为了不和提供端到端流量控制,能够建议“慢启动”协议。
对于该协议的改进主要集中在如何减小TCP报文重传方面,目前在TCP中每传输一个报文都要求接收方进行确认,大量短而频繁的确认报文给网络带来了不少开销。所以采起了延迟ACK策略来减小ACK的数量,就是接收方收到一个报文之后,不会当即发送ACK,而是等待1~200ms,这期间如有回送数据报文就捎带确认,但收到两个连续数据报文或者等待超时则发送一个独立确认。有效减小了ACK的数量,改善了TCP的总体性能。
[4]
TCP滑动窗口机制html

咱们能够大概看一下上图的模型:编程
首先是AB之间三次握手创建TCP链接。在报文的交互过程当中,A将本身的缓冲区大小(窗口大小)3发送给B,B同理,这样双方就知道了对端的窗口大小。缓存
A开始发送数据,A连续发送3个单位的数据,由于他知道B的缓冲区大小。在这一波数据发送完后,A就不能再发了,需等待B的确认。服务器
A发送过来的数据逐渐将缓冲区填满。网络
这时候缓冲区中的一个报文被进程读取,缓冲区有了一个空位,因而B向A发送一个ACK,这个报文中指示窗口大小为1。框架
A收到B发过来的ACK消息,而且知道B将窗口大小调整为1,所以他只发送了一个单位的数据而且等待B的下一个确认报文。socket
如此反复。分布式
什么是滑动窗口协议?
一图胜千言,看下面的图。简单解释下,发送和接受方都会维护一个数据帧的序列,这个序列被称做窗口。发送方的窗口大小由接受方肯定,目的在于控制发送速 度,以避免接受方的缓存不够大,而致使溢出,同时控制流量也能够避免网络拥塞。下面图中的4,5,6号数据帧已经被发送出去,可是未收到关联的 ACK,7,8,9帧则是等待发送。能够看出发送端的窗口大小为6,这是由接受端告知的(事实上必须考虑拥塞窗口cwnd,这里暂且考虑 cwnd>rwnd)。此时若是发送端收到4号ACK,则窗口的左边缘向右收缩,窗口的右边缘则向右扩展,此时窗口就向前“滑动了”,即数据帧10 也能够被发送。函数

下面就滑动窗口协议作出更详细的说明,这里为了简单起见设定发送方窗口大小为2,接受方大小为1。看下面图:post
一:初始态,发送方没有帧发出,发送窗口先后沿相重合。接收方0号窗口打开,等待接收0号帧;
二:发送方打开0号窗口,表示已发出0帧但尚确认返回信息。 此时接收窗口状态不变;
三:发送方打开0、1号窗口,表示0、1号帧均在等待确认之列。至此,发送方打开的窗口数已达规定限度,在未收到新的确认返回帧之 前,发送方将暂停发送新的数据帧。接收窗口此时状态仍未变;
四:接收方已收到0号帧,0号窗口关闭,1号窗口打开,表示准备接收1号帧。此时发送窗口状态不 变;
五:发送方收到接收方发来的0号帧确认返回信息,关闭0号窗口,表示从重发表中删除0号帧。此时接收窗口状态仍不变
六:发送方继续发送2号帧,2号窗口 打开,表示2号帧也归入待确认之列。至此,发送方打开的窗口又已达规定限度,在未收到新的确认返回帧以前,发送方将暂停发送新的数据帧,此时接收窗口状态 仍不变;
七:接收方已收到1号帧,1号窗口关闭,2号窗口打开,表示准备接收2号帧。此时发送窗口状态不变;
八:发送方收到接收方发来的1号帧收毕的确认信 息,关闭1号窗口,表示从重发表中删除1号帧。此时接收窗口状态仍不变。
1比特滑动窗口协议?
上面说的只是滑动窗口协议的理论,实际应用中又有不一样。首先就是停等协议(stop-and-wait),这时接受方的窗口和发送方的窗口大小都是1,1 个比特就够表示了,因此也叫1比特滑动窗口协议。发送方这时天然发送每次只能发送一个,而且必须等待这个数据包的ACK,才能发送下一个。虽然在效率上比 较低,带宽利用率明显较低,不过在网络环境较差,或是带宽自己很低的状况下,仍是适用的。看下面的流程图:

后退n协议?
停等协议虽然实现简单,也能较好的适用恶劣的网络环境,可是显然效率过低。因此有了后退n协议,这也是滑动窗口协议真正的用处,这里发送的窗口大小为n,接受方的窗口仍然为1。具体看下面的图,这里假设n=9:
首先发送方一口气发送10个数据帧,前面两个帧正确返回了,数据帧2出现了错误,这时发送方被迫从新发送2-8这7个帧,接受方也必须丢弃以前接受的3-8这几个帧。
后退n协议的好处无疑是提升了效率,可是一旦网络状况糟糕,则会致使大量数据重发,反而不如上面的停等协议,实际上这是很常见的,具体能够参考TCP拥塞控制。
选择重传协议?
后退n协议的另一个问题是,当有错误帧出现后,老是要重发该帧以后的全部帧,毫无疑问在网络不是很好的状况下会进一步恶化网络情况,重传协议即是用来解 决这个问题。原理也很简单,接收端总会缓存全部收到的帧,当某个帧出现错误时,只会要求重传这一个帧,只有当某个序号后的全部帧都正确收到后,才会一块儿提 交给高层应用。重传协议的缺点在于接受端须要更多的缓存。
TCP滑动窗口易错处
前段时间研究分布式时写了一个可扩展的服务器组程序,服务器组之间通讯时总是达不到想要的性能。后来抓包排查,原来是TCP滑动窗口引发的问题,原本是很基础的东西,奈何当初没有太在乎,致使错误的产生,如今详细写出来,忘不太清楚者警戒!
滑动窗口的基本状况我有必要废话一下。TCP通讯为了保证可靠性,每次发送的数据都须要获得对方的ACK才确认对方收到了(仅保证对方TCP接收缓冲收到数据了,但不保证对方应用程序取到数据了),这时若是每次发送一次就要停下来等着对方的ACK消息,显然是一种极大的资源浪费和低下的效率,这时就有了滑动窗口的出现。
发送方的滑动窗口维持着当前发送的帧序号,已发出去帧的计时器,接收方当前的窗口大小(由接收方ACK通知,大致等于接收缓冲大小-未处理的消息包),接收方滑动窗口保存的有已接收的帧信息、期待的下一帧的帧号等,至于滑动窗口的具体工做原理这里就不说了。
一个socket有两个滑动窗口(一个sendbuf、一个recvbuf),两个窗口的大小是经过setsockopt函数设置的,如今问题就出在这里,经过抓包显示,设置的窗口大小没有生效,最后排查发现setsockopt函数是后来加上的,写到了listen函数的后面,这样每次accept出的socket并无继承获得主socket设置的窗口大小,无语啊……
解决办法:setsockopt函数提早到listen函数以前,这样在服务器程序启动监听前recvbuf就已经有了,accept后的连接获得的就是recvbuf了,启动程序运行,抓包显示窗口已是指定的大小了。
网络编程其实很简单,任何人均可以写出一套本身的服务器框架,可是细节决定成败,性能的高低有时候就是几个小细节决定的(固然这里说的这个问题是个编程错误,不属于可优化的细节问题)
前言
你如今的努力,是为了之后有更多的选择。
在上一篇文章经过“表白”方式,让咱们快速了解网络七层协议了解了网络七层协议。
接下来咱们要把重心放在网络传输的可靠性上面。一块儿来看TCP协议,它是如何解决网络传输不可靠的问题。这其中有个很关键的部分,就是咱们的滑动窗口协议。
从工程学角度上,咱们来看一看滑动窗口协议,它到底解决了一个怎样的问题?
滑动窗口协议:
- TCP协议的使用
- 维持发送方/接收方缓冲区
缓冲区是 用来解决网络之间数据不可靠的问题,例如丢包,重复包,出错,乱序
在TCP协议中,发送方和接受方经过各自维护本身的缓冲区。经过商定包的重传机制等一系列操做,来解决不可靠的问题。
问题一:如何保证次序?
提出问题:在咱们滑动窗口协议以前,咱们如何来保证发送方与接收方之间,每一个包都能被收到。而且是按次序的呢?
发送方发送一个包1,这时候接收方确认包1。发送包2,确认包2。就这样一直下去,知道把数据彻底发送完毕,这样就结束了。那么就解决了丢包,出错,乱序等一些状况!同时也存在一些问题。问题:吞吐量很是的低。咱们发完包1,必定要等确认包1.咱们才能发送第二个包。
问题二:如何提升吞吐量?
提出问题:那么咱们就不能先连发几个包等他一块儿确认吗?这样的话,咱们的速度会不会更快,吞吐量更高些呢?
如图,这个就是咱们把两个包一块儿发送,而后一块儿确认。能够看出咱们改进的方案比以前的好不少,所花的时间只是一个来回的时间。接下来,咱们还有一个问题:改善了吞吐量的问题
问题三:如何实现最优解?
问题:咱们每次须要发多少个包过去呢?发送多少包是最优解呢?
咱们能不能把第一个和第二个包发过去后,收到第一个确认包就把第三个包发过去呢?而不是去等到第二个包的确认包才去发第三个包。这样就很天然的产生了咱们"滑动窗口"的实现。
在图中,咱们可看出灰色1号2号3号包已经发送完毕,而且已经收到Ack。这些包就已是过去式。四、五、六、7号包是黄色的,表示已经发送了。可是并无收到对方的Ack,因此也不知道接收方有没有收到。八、九、10号包是绿色的。是咱们尚未发送的。这些绿色也就是咱们接下来立刻要发送的包。 能够看出咱们的窗口正好是11格。后面的11-16尚未被读进内存。要等4号-10号包有接下来的动做后,咱们的包才会继续往下发送。
正常状况
能够看到4号包对方已经被接收到,因此被涂成了灰色。“窗口”就往右移一格,这里只要保证“窗口”是7格的。 咱们就把11号包读进了咱们的缓存。进入了“待发送”的状态。八、9号包已经变成了黄色,表示已经发送出去了。接下来的操做就是同样的了,确认包后,窗口日后移继续将未发送的包读进缓存,把“待发送“状态的包变为”已发送“。
丢包状况
有可能咱们包发过去,对方的Ack丢了。也有可能咱们的包并无发送过去。从发送方角度看就是咱们没有收到Ack。
发生的状况:一直在等Ack。若是一直等不到的话,咱们也会把读进缓存的待发送的包也一块儿发过去。可是,这个时候咱们的窗口已经发满了。因此并不能把12号包读进来,而是始终在等待5号包的Ack。
若是咱们这个Ack始终不来怎么办呢?
超时重发
这时候咱们有个解决方法:超时重传
这里有一点要说明:这个Ack是要按顺序的。必需要等到5的Ack收到,才会把6-11的Ack发送过去。这样就保证了滑动窗口的一个顺序。
这时候能够看出5号包已经接受到Ack,后面的六、七、8号包也已经发送过去已Ack。窗口便继续向后移动。
参考:TCP 滑动窗口协议
参考:滑动窗口协议
参考:一篇带你读懂TCP之“滑动窗口”协议