TCP/IP:滑动窗口

TCP的优势

从传输数据来讲,TCP/UDP以及其他协议都可以完成数据的传输,从一端传输到另外一端,TCP比较出众的一点是提供一个可靠地,流控的数据传输,所以实现起来要比其他协议复杂得多。

1、Reliability:提供TCP的可靠性,TCP的传输要保证数据能够准确到达目的地,如果不能,需要能检测出来并且重新发送数据。

2、Data Flow Control:提供TCP的流控特性,管理发送数据的速率,不要超过设备的承载能力。

为了实现以上两点,TCP实现了很多细节的功能来保证数据传输,比如,滑动窗口适应系统,超时重传机制,累计ACK等。

 

滑动窗口的引入

  • 窗口机制的分类:

     在TCP协议当中窗口机制分为两种:

  1. 固定的窗口大小
  2. 滑动窗口
  • 固定窗口存在的问题

我们假设这个固定窗口的大小为1,也就是说每次只能发送一个数据,只有接收方对这个数据进行了确认后才能发送第二个数据。在图中我们可以看出,发送方每发送一个数据接收方就要给发送方发送一个ACK对这个数据进行确认。只有接受了这个确认数据以后发送方才能传输下一个数据。

存在的问题:

  • 如果窗口过小,当传输比较大的数据的时候需要不停的对数据进行确认,这个时候就会造成很大的延迟。
  • 如果窗口过大,我们假设发送方一次发送100个数据,但接收方只能处理50个数据,这样每次都只对这50个数据进行处理。发送方下一次还是发送100个数据,但接收方还是只能处理50个数据。这样就避免不了不必要的数据来拥塞我们的链路。

因此,我们引入了滑动窗口。

 

滑动窗口

1.概述

滑动窗口通俗来讲就是一种流量控制技术。

它本质上是描述接收方的TCP数据报缓冲区大小的数据,发送方根据这个数据来计算自己最多能发送多长的数据,如果发送方收到接收方的窗口大小为0的TCP数据报,那么发送方将停止发送数据,等到接收方发送窗口大小不为0的数据报的到来。

2.工作原理

 第一次发送数据这个时候的窗口大小是根据链路带宽的大小决定的。

假设这个时候的窗口是3,这个时候接收方收到数据以后会对数据进行确认,告诉发送方我下次希望收到的数据是多少。

在上图中:我们看到接收方发送的ACK=3(这是对方发送序列2的回答确认,下一次接收方期望收到的是3序列信号),这个时候发送方收到这个数据以后就知道我第一次发送的3个数据对方只收到了两个,就知道第三个数据对方没有收到,下次发送的时候就会从第三个数据开始发。这时候窗口大小就变为了2。

如下图所示:

看到接收方发送的ACK是5就表示他下一次希望收到的数据是5,发送方就知道我刚才发送的两个数据对方收到了,这个时候开始发送第5个数据。

当链路变好或变坏,这个窗口还会发生变化,并不是第一次协商好之后就永远不会变化了。

如果发送方有1、2、3 3个报文段,先发送了2,3两个报文段,但是接收方期望收到1报文段,这个时候2,3报文段就只能放在缓存中等待报文1的空洞被填上,如果报文1,一直不来,报文2、3也将被丢弃,如果报文1来了,那么会发送一个ACK对这3个报文进行一次确认。

 

问题

1.死锁状态

(1)概述:当接收端向发送端发送零窗口报文段后不久,接收端的接收缓存又有了一些存储空间,于是接收端想发送端发送了Windows size=2的报文段,然而这个报文段在传输过程中丢失了。发送端一直等待收到接收端发送的非零窗口的通知,而接收端一直等待发送端发送数据,这样就死锁了。

(2)解决方法:TCP为每一个连接设有一个持续计时器。只要TCP连接的一放收到对方的零窗口通知,就启动持续计时器,若持续计数器设置的时间到期,就发送一个零窗口探测报文段(仅携带1字节数据),而对方就在确认这个探测报文段时给出了现在的窗口值。

 

2.TCP报文段的发送时机(传输效率问题)

可以用以下三种不同的机制控制TCP报文段的发送时机:

(1)TCP维持一个变量MSS,等于最大报文段的长度。只要缓冲区存放的数据达到MSS字节时,就组装成了一个TCP报文段发送出去。

(2)由发送方的应用进程指明要发送的报文段,即:TCP支持推送操作。

(3)发送方的一个计时器期限到了,这个时候就把当前已有的缓存数据装入报文段(但长度不能超过MSS)发送出去。

 

3.Nagle算法(控制TCP报文段的发送时机)

(1)主旨:避免大量发送小包。

(2)初衷:避免发送大量的小包,防止小包泛滥于网络,在理想情况下,对于一个TCP连接而言,网络上每次只能有一个小保存在。它更多的是端到端意义上的优化。

【CORK算法:提高网络利用率,理想情况下,完全避免发送小包,仅仅发送满包以及不得不发的小包】

发送方将第一个数据字节发送出去,把后面到达的数据字节缓存起来。当发送方接收到第一个数据字符的确认后,再把发送缓存中的所有数据组装成一个报文段再发送出去,同时继续对随后到达的数据进行缓存。只有在收到对前一个报文段的确认之后,才继续发送下一个报文段。规定一个TCP连接最多只能有一个未被确认的未完成的小分组,在该分组的确认到达之前不能发送其他的小分组。当数据到达较快而网络速率较慢时,用这样的方法可以明显的减少所用的网络带宽。

  Nagle算法还规定:当达到的数据已经达到发送窗口大小的一半或者已经达到报文段的最大长度时,就可以立即发送一个报文段。

 

4.糊涂窗口综合症(接收端糊涂,网络上小包泛滥的原因之一)

(1)概述:TCP接收方的缓存已满,而交互式的应用进程一次只从接收缓存区中读取1字节(这样就使接收缓存空间仅腾出1字节),然后向发送方发送确认,并把窗口设置为1个字节(但发送的数据报为40字节的话),然后,发送方又发来1字节的数据(发送方的IP数据报是41字节),接收方发回确认,仍将窗口设置为1个字节,这样,网络效率就会很低
(2)解决办法

 a.你糊涂我不糊涂法。即Nagle算法。

可让接收方等待一段时间,使得或者  接收缓存已有足够的空间容纳一个  最长的报文段,或者等到接收方缓存已有一半的空闲空间。只要出现这两种情况,接收方就发回确认报文,并向发送方通知当前的窗口大小。此外,发送方也不要发送太小的报文段,而是把数据报文积累为足够大的报文段或达到接收方缓存的空间的一半大小。


b.治疗接收端的糊涂(其中一种机制是延迟ACK(还有其它机制,例如不发送小窗口通告等))

 对于接收方而言, 延迟ACK可以拖延ACK发送时间,进而延迟窗口通告,在这段时间内,接收窗口有机会进一步放大

 对于发送方而言, 不理会接收端的小窗口通告等于说不马上1发送小包,小包有时间积累成大包

 

参考文献:

https://blog.csdn.net/m0_37962600/article/details/79951780

http://www.javashuo.com/article/p-cimttgrd-mt.html