1. 引入滑动窗口的原因
- 如果没有滑动窗口,TCP每发送一个数据,都需要等待这一次确认应答。只有收到了上一个数据包的应答,才能再发送下一个。这样效率太低了
- 当引入了滑动窗口机制后,就可以采取累计确认机制。
- TCP引入了窗口这个概念,有了窗口,就可以指定窗口大小,窗口大小是指无需等待确认应答,而可以继续发送数据的最大值。
- 如图,假设窗口大小是3个TCP段。即使ACK 600确认应答报文丢失,也没关系,因为可以通过下一个确认应答进行确认。只要发送方收到了ACK700确认应答,就意味着700之前的所有数据接收方都收到了。这就是累计确认。
2. 累计确认机制
滑动窗口是缓存的一部分,用来暂时存放字节流。 发送方和接收方各有一个窗口,接收方通过TCP报文段中的窗口字段告诉发送方自己的窗口大小。发送方根据这个值和拥塞窗口信息来设置自己的窗口大小。
发送窗口内的字节都允许被发送,接收窗口的字节都允许被接收,如果发送窗口左部的字节已经发送并且收到了确认,那么就将发送窗口向右滑动一定距离。直到左部第一个字节不是已发送并且已确认的状态;接收窗口的滑动窗口类似,接收窗口左部字节已经发送确认并交付主机,就右滑动接收窗口。
接收窗口只会对窗口内最后一个按序达到的字节进行确认。例如接收窗口已经收到了字节为{31,34,35},其中31是按序达到,则只对31确认。发送方得到这个字节的确认后,就知道这个字节前面的所有字节都被接收了。
3. 窗口大小由哪一方决定
- TCP头部有一个Window字段,指明了窗口大小
- 这个字段是接收端告诉发送端自己还有多少缓存区可以接收数据。于是发送端就可以根据接收端的处理能力来发送数据,而不会导致接收端处理不过来
3.1 发送方的滑动窗口
- SND.WND:表示发送窗口的大小(大小由接收方指定的)
- SND.UNA:是一个绝对指针,指向已发送但未确认的第一个字节的序列号。
- SND.NXT:是一个绝对指针,指向未发送但处在可发送范围内的第一个字节的序列号。
- 发送方的可用窗口大小=SND.WND-(SND.NXT-SND.UNA)
3.2 接收方的滑动窗口
- RCV.WND:表示接收创建大小,他会通告给发送方
- RCV.NXT:是一个指针,它指向期望从发送方发送过来的下一数据字节的序列号。
3.3 操作系统缓冲区和滑动窗口的关系
- 发送窗口和接收窗口中存放的字节数,都是存放在操作系统内存缓冲区中的。
- 接收方的应用程序不能及时地读取缓冲区中的内容的话,则会影响接收方的滑动窗口的大小(RCV.WND)。
- 假设缓冲区大小为200,刚开始接收方的滑动窗口大小则也是200。
- 发送方按序发送过来了100字节的数据,并且接收方也成功接收到了。但是此时接收方应用程序只能处理50字节的数据
- 根据滑动窗口的原理可知RCV.NXT应该会向右移动100字节。但是还有50字节需要放在缓冲区中等待应用程序处理。因为缓冲区就那么大(200字节),所以此时RCV.WND的大小就变成了150。可用大小也是150.
- 接收端通过TCP的Window字段告诉发送端自己还能接收150字节。则也会影响发送端的发送窗口大小。
3.4 什么是窗口关闭
- TCP通过接收方指明希望从发送方接受的数据大小(窗口大小)来进行流量控制。
- 如果窗口大小为0时,则会阻止发送方给接收方传递数据,直到窗口变为非0为止。这个过程就是窗口关闭。
- 当接收方的接收窗口腾出来了,则就会发送一个ACK报文,将window字段设置为非0.
3.5 窗口关闭潜在的风险
- 接收方向发送方告知窗口大小是通过ACK报文来通告的。
- 如果发生了窗口关闭,接收方处理完数据后,会向发送方通告一个窗口非0的ACK报文告知发送方窗口大小已经变更。但如果这个通告窗口的ACK报文在网络上丢失,那么发送方的就一直不能发送数据。
3.6 如何解决窗口带来的潜在风险
-
当发送方收到了零窗口通知,会启动一个持续计时器。
-
当持续计时器超时后,发送方就会主动发送一个窗口探测报文。
-
而接收方收到这个探测报文时,会给发送方回复一个确认,并给出自己现在的可接收窗口大小
-
如果接收窗口仍然为0,那么收到这个报文的一方就会重新启动持续计时器
-
如果接收窗口不是0,那么就可以发送数据了。
-
- 窗口探测的次数一般是3次,每次大约30-60秒,超过3次还是0的话,有的TCP实现就会发RST报文来中断连接
3.7 如果接收方每次回应的窗口很小怎么办
- 假设接收方回应的窗口过小,比如1字节。发送方该不该继续发?(TCP头部就20字节了)
- 解决办法一:让接收方不告知小窗口
- 如:当窗口大小<min(MSS,缓存空间/2)。就向发送方通知窗口为0。这样就阻止发送方再发数据过来。
- 解决方法二:避免发送方在小窗口发送数据
- 只有等到窗口大小>=MSS才发送数据
3.8 Nagle算法如何避免大量TCP小数据报文的传输?
- Nagle算法的策略
- 没有已发送未确认报文时,立刻发送数据
- 存在未确认时,直到没有已发送未确认报文或数据长度达到MSS大小时,再发送数据
- 不满足上述条件的任意一条,发送方就一直囤积数据,直到满足条件位置
- 所以这种方式不适合网络实时游戏,比如王者荣耀
- 系统默认开启Nagle算法,只能通过Socket设置TCP_NODELAY选项来关闭这个算法(即无法全局关闭)
3.8 什么是流量控制
- 接收方通过TCP的window字段去告知发送方自己的实际接收能力,然后发送方根据这个实际接收能力控制自己发送的数据量,这就是流量控制。
- 流量控制是基于滑动窗口实现的。