计算机网络核心知识--1.5 TCP的滑动窗口

了解滑动窗口之前,先了解两个概念,即RTT和RTO
(1)RTT:发送一个数据包到收到对应的ACK,所花费的时间。
简单来说,就是我发送一个数据包,然后对端回应一个ACK,那么当我接收到ACK之后,就能计算出从我发出包到接到回应过了多久,这个时间就是RTT。
(2)RTO:重传时间间隔。
TCP在发送一个数据包之后,会启动一个重传定时器,而RTO就是这个定时器的重传时间。再通俗一点地讲,我一开始预先算一个定时器时间,如果你回复了ACK,那重传定时器就自动失效,也就是说不用重传了。如果没有回复给我ACK,然后RTO定时器的时间又到了,我就重传。由于RTO是本次发送当前数据包所预估的超时时间,那么RTO就需要一个很好的算法来统计,来更好地预测这次的超时时间。RTO不是固定写死的配置,而是经过RTT计算出来的,有了RTT才能计算出RTO。

前面我们了解到,TCP会将数据拆分成段进行发送,出于效率和传输速度的考虑,我们不可能等一段一段数据去发送,即等到上一段数据被确认之后再发送下一段数据,这个效率是非常低的,我们是要实现对数据的批量发送。TCP必须要解决可靠传输,以及包乱序的问题,所以TCP需要知道网络实际的数据处理带宽,或是数据处理速度,这样才不会引起网络拥塞而导致丢包。

TCP使用滑动窗口做流量控制与乱序重排。

TCP的滑动窗口主要有两个作用:
(1)保证TCP的可靠性
(2)保证TCP的流控特性

之前学过的TCP报文中,有一个字段叫Window,我们也可以叫做AdvertisedWindow,用于接收方通知发送方自己还有多少缓冲区可以接收数据,发送方根据接收方的处理能力来发送数据,不会导致接收方处理不过来,这便是流量控制。
同时滑动窗口机制还体现了TCP面向字节流的设计思路。

窗口数据的计算过程如下:
计算机网络核心知识--1.5 TCP的滑动窗口
上图左边部分是TCP协议的发送端缓冲区,右边部分是接收端的缓存区,左边往右边发数据。两个图中下面的长方形表示要发送的数据流,里面假设装满了数据,并且需要按照顺序从左往右发送或者接收。假设对应的数据段的位置序号也是从左到右增长的。
对于发送方来讲,LastByteAcked指向收到的连续最大的ACK的位置,也就是从左端算起,连续已经被接收端的程序发送ack回执确认已收到的sequence number。而LastByteSent指向已发送的最后一个字节的位置,该位置只是发出去了,但是还没有收到ACK的回应。而LastByteWritten指向上层应用已写完的最后一个字节的位置,即当前程序已经准备好的需要发送的最新的数据量,“2”表示的部分是发送出去但是还没有收到确认的,“1”表示的部分是发送出去并且已经收到接收端的确认的了。我们可以看到,从LastByteAcked到LastByteWritten都是没有出现间隔的,都是连续的。
对于接收方来讲,LastByteRead指向上层应用已经读完的最后一个字节的位置,也就是说,我收到了发送方的数据,并且已经处理,并且已经给了回执了的数据的最后一个位置,而NextByteExpected指向收到的连续最大的sequence的位置,比如“5”部分我已经收到了,但是还没有给你发送回执,而LastByteRcvd是指向已收到的最后一个字节的位置,可以看到,NextByteExpected和LastByteRcvd之间有一些sequence还没有到达,对应的是空白的区域。
此时我们可以根据上面的数值计算出接收方的AdvertisedWindow的大小,之后回发给发送方,让其计算出发送方的剩余可发送的数据量的大小,
(1)AdvertisedWindow = MaxRcvBuffer - (LastByteRcvd - LastByteRead)
该公式可以计算出接收方还能处理的数据的量。
MaxRcvBuffer是指接收方能接收的最大数据量,也可以理解为接收方缓存池的大小。
(LastByteRcvd - LastByteRead)表示的是,我们当前的接收方已为接收到的数据,或者还没有接收到的预定的数据留下的空间,当前的这些空间已经占据了一定的缓存了,我们就将最大空间减去这些已经占据的空间,就得到我们还能够接收到的数据量,进而我们就能够将这个AdvertisedWindow告知发送端。

(2)EffectiveWindow = AdvertisedWindow - (LastByteSent-LastByteAcked)
而发送端根据ack中的AdvertisedWindow的值,就需要保证,LastByteSent减去LastByteAcked的值要小于或等于AdvertisedWindow,即已发送且待确认的数据量要小于接收方的window大小,而窗口剩余可发送数据大小,即EffectiveWindow的大小。

滑动窗口的基本原理
对于TCP会话的发送方,任何时候在其发送缓存内的数据,都可以分为4类,
计算机网络核心知识--1.5 TCP的滑动窗口
(1)已发送,已收到端的回应的;
(2)已发送,还未收到端的回应的;
(3)未发送,但对端允许发送的;
(4)未发送,且由于达到了window大小,对端不允许发送的数据。

其中,第二部分和第三部分,这两个部分的数据所组成的这么一个连续空间,就称为发送窗口。

当收到接收方新的ACK,对于发送窗口中后续字节的确认时,窗口就会进行滑动,滑动原理如下图所示。我们先从原滑动窗口说起,它由虚线部分组成。前面我们已经知道,滑动窗口里面包含了已经发送但还没有收到接收方回应的数据 和 未发送,但允许向接收方发送的数据,我们假设原先滑动窗口的边界是从32到51,我们假设已发送但未被确认的序号是3240,而4151为未发送但对端允许发送的,此时,只要收到对端大于32的序号的ACK,即收到32~40之间某个ACK序号的回执的时候,咱们的滑动窗口才会发生移动,而此时序号大于或等于52的数据,即窗口外的数据,是不能发送的。这里假设收到对端序号为36的ACK回执,则滑动窗口会向右移动4位,到36这个地方,进而我们的程序就能够发送序号为52到55的数据了。
计算机网络核心知识--1.5 TCP的滑动窗口

对于TCP会话的接收方来说,在某一时刻,在其接收缓存内会在3种状态,
(1)已接收。并且已经发送回执的状态;
(2)未接收,但可以接收的状态,即准备接收的状态;
(3)未接收,并且不能接收的状态,因为达到窗口的阈值了。
由于ACK直接由TCP栈回复,默认没有应用延迟,因此不存在已接收但未回复这种状态。

其中,未接收并且准备接收这一段空间,就称为接收窗口。接收窗口的滑动机制和前面发送方的一致。
计算机网络核心知识--1.5 TCP的滑动窗口
经过上面的学习,我们得知TCP最基本的传输可靠性来源于确认重传机制,TCP滑动窗口的可靠性也是建立在确认重传的基础上的。发送窗口只有收到接收端对于本段发送窗口内字节的ACK确认,才会移动发送窗口的左边界,接收窗口只有在前面所有的段都确认的情况下,才会移动左边界。当在前面还有字节未接收,但收到后面的字节的情况下,窗口是不会移动的,并不对后续字节确认,以此确保对端会对这些数据进行重传。

滑动窗口的大小可以依据一定策略动态调整,应用会根据自身处理能力的变化,通过本端TCP接收窗口的大小的控制,来实现对端的发送窗口的流量控制。

问题:
1,什么是滑动窗口?
答:简单来说,滑动窗口就是一种流量控制技术,用于接收方通知发送方自己还有多少缓冲区可以接收数据,发送方根据接收方的处理能力来发送数据,不会导致接收方处理不过来。

2,滑动窗口有什么用?
答:一是保证TCP的可靠性;二是保证TCP的流控特性。

3,滑动窗口怎么用?其原理是什么?
答:发送窗口只有收到接收端对于本段发送窗口内字节的ACK确认,才会移动发送窗口的左边界,接收窗口只有在前面所有的段都确认的情况下,才会移动左边界。当在前面还有字节未接收,但收到后面的字节的情况下,窗口是不会移动的,并不对后续字节确认,以此确保对端会对这些数据进行重传