一文带你搞定TCP流量控制 tcp流量控制算法

摘要

  1. 理想的流量控制
  2. 实际的流量控制
  3. 窗口关闭
  4. 糊涂窗口综合征

理想的流量控制

什么是流量控制?

流量控制就是发送方不能无脑的给接收方发送数据,它需要根据接收方的处理能力来发送数据。

理想下的流量控制?

理想意味着在实际中不存在,这里只是简单的说一下流量控制的作用,我们假设的理想通信发生条件为:

  • 客户端是接收方、服务端是发送方
  • 接收窗口和发送窗口相同,为200
  • 接收方和发送方在通信过程中始终保持相同的窗口大小
  1. 客户端发送请求数据
  2. 服务端收到请求后,发送确认报文和80字节的数据,可用窗口减少为120(200-80),SND.NXT指针变为321,表示发送方下次发送序列号为321的数据
  3. 客户端收到数据后,接收窗口右移80字节,RCV.NXT为321,表示客户端段期望下一个收到的报文的序列号为321,并且发送ACK报文给服务端
  4. 服务端发送120字节的数据,可用窗口变为0,服务端无法发送数据
  5. 客户端收到120字节的数据,接收窗口右移120字节,接着发送ACK报文给客户端
  6. 服务端收到序列号为241(长度为80字节)的ACK报文,因此SND.UNA右移80字节变为321(241+80),可用窗口变为80字节
  7. 服务端收到序列号321(长度为120字节)的ACK报文,因此SND.UNA右移120字节变为441(321+120),可用窗口变为200字节
  8. 服务端发送160字节的数据,可用窗口变为40字节,并且SND.NXT右移160个字节变为601(441+160)
  9. 客户端在收到数据后,接收窗口右移160字节,接着发送ACK报文给服务端
  10. 服务端在收到ACK报文后,SND.UNAK右移160字节,并且可用窗口再次恢复为200字节

实际的流量控制

操作系统缓冲区和滑动窗口的关系

由于我们发送的数据都是暂存在操作系统的内存缓冲区,所以滑动窗口会收操作系统缓冲区的影响:

  • 操作系统内存缓冲区会动态调整,影响窗口大小
  • 如果应用程序如果无法及时从缓冲区读取内容,也会导致缓冲区减少,此时滑动窗口需要适当调小,避免缓冲区内容溢出。

应用程序无法及时读取缓存

假设以下场景:

  • 客户端为发送方,服务端为接收方,初始化发送窗口和接收窗口都为360
  • 服务端繁忙,收到客户端数据后,应用层无法及时读取数据
  1. 客户端发送140字节的数据,可用窗口变为220(360-140),SND.NXT右移140变为141(1+140)
  2. 服务端收到140字节的数据,应用程序只读取了40节数据,还有100字节存在于缓冲区中,于是接收窗口变为260(360-100),服务端在给客户端发送ACK报文时会把新的窗口大小告知客户端。
  3. 客户端在收到ACK报文以后,发送窗口减少为260,并且由于收到了数据的ACK,因此SND.UNA会右移140个字节变为141,此时可用窗口为260
  4. 客户端继续发送180字节的数据,SND.NXT变为321,可用窗口变为80(260-80)
  5. 服务端在收到数据后,应用进程没有读取任何数据,于是接收窗口从260缩小为80(260-180),并且在发送ACK报文时告知客户端
  6. 客户端收到ACK报文以后,会将发送窗口减少为80,可用窗口此时也是80,因此到这里客户端最多只能发送80字节的数据给服务端
  7. 客户端发送最后80字节的数据给服务端,可用窗口变为0
  8. 服务端收到80字节的数据后,应用进程依然没有读取任何数据,于是接收窗口减少为0,并在发送ACK报文告知客户端
  9. 客户端收到ACK报文以后,发现窗口大小为0,因此将发送窗口减少为0。

这里会有个问题,窗口变为0也就是发生了窗口关闭。

操作系统缓冲区变化

当服务器资源紧张,操作系统有可能会减少缓冲区的大小,如果此时应用程序还无法读取数据,那么将会出现数据包丢失现象。

  1. 客户端发送140字节数据,可用窗口变为220(360-140)
  2. 服务端收到140字节数据后,但是系统资源紧张,操作系统减少120字节的缓冲区,并且应用层没有读取任何数据,于是接收窗口变为100(360-120-140),然后发送ACK报文告知客户端
  3. 在收到服务端的ACK报文前,客户端又发送了180字节的数据,可用窗口减少到40
  4. 服务器收到180字节的数据后,由于接收窗口只有100字节,超出了缓冲区的大小,因此会丢弃该数据包
  5. 客户端此时收到之前的ACK报文,会将发送窗口减少为100,此时可用窗口出现了负值-80 =(100 -(321 - 141))

在上述情况中,减少缓存先于收缩窗口发生,出现丢包现象

为了防止上述情况,TCP规定是先收缩窗口,过段时间再减少缓存,这样避免丢包。

窗口关闭

什么是窗口关闭

窗口大小为0,阻止发送方给接收方发送数据,直到窗口变为非0才能恢复发送。

窗口关闭的危险

窗口关闭以后发送端无法发送数据给接收端,只有当接收端处理完数据以后,这时候窗口回复,发送ACK报文信息给客户端,客户端才能恢复发送。但是一旦该ACK报文丢失,那么发送方会一直等待接收方的非0窗口通知,接收方也一直在等待发送方的数据,容易造成死锁现象。

如何解决窗口关闭带来的死锁?

只要TCP连接的一方收到对方0窗口的通知,就启动计时器,如果计时器超时就会发送窗口探测报文给对端,对端会给出自己的接收窗口大小。

  • 如果收到的窗口依然为0,发送方重启启动持续计时器
  • 如果收到的窗口不为0,恢复正常发送

窗口探测的次数一般为3次,每次大约30~60s。如果三次以后接收窗口还是0,有的TCP实现就会发送RST报文来中止连接。

糊涂窗口综合征

什么是糊涂窗口综合征?

如果接收方太忙,来不及取走缓冲区的数据,发送方的窗口会越来越小,最后如果接收方空出几个字节并告诉发送方现在有几个字节的窗口,发送方便会发送这几个字节,这就是糊涂窗口综合征。

糊涂窗口综合征的缺点?

TCP+IP头部大约有40个字节,为了几个字节数据,需要加上头部相对大的开销,性价比极低。

糊涂窗口综合征的原因是?

  • 接收方会告知发送方一个小的窗口
  • 发送方可以发送小数据

如何避免接收方告知小窗口?

接收方在告知窗口时会采取一种策略:

  • 当窗口大小小于min(MSS,缓存空间/2),就会向发送方告知窗口为0,避免发送方发送数据,等到接收方处理完一些数据后,窗口大小>=MSS或者有一半以上缓存空间可以使用时,就可以把窗口打开让发送方发送数据。

如何避免发送方发送小数据?

发送方在发送数据时采用Nagle算法,该算法的思路是延时处理,满足以下两个条件才可以发送数据:

  • 窗口大小>=MSS或是数据大小>=MSS
  • 收到之前发送数据的ACK报文

对于telnet或ssh这种小数据包交互场景的应用程序,需要关闭Nagle算法。

// 在Java中,对Socket进行以下设置可以关闭Nagle算法。
Socket socket =  new Socket();
socket.setTcpNoDelay(true);

相关文章

3000字讲讲TCP协议,握手挥手不是你想的那么简单

专注于Java领域优质技术,欢迎关注作者: tobe 来自:tobe的呓语上一次讲了 UDP 协议,从这次开始,就要讲 TCP 协议了,因为 TCP 协议涉及到的东西很多,一篇文章概括不完,所以我把...

Java编程-TCP JAVA编程手机软件

1.1. Java的TCP面向连接, 数据安全, 区分服务器端和客户端.TCP分为Socket(客户端)和ServerSocket(服务端)需要分别建立客户端和服务器端客户端和服务端建立连接后,通过S...

Java网络编程---TCP通信 java的tcp通讯协议数据传输

TCP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket对象,从而在通信两端形成网络虚拟链路,一旦建立了虚拟的网络链路,两端的程序就可以通过虚拟链路进行通信Java对基于TCP协议的网...

4000 字详解TCP超时与重传,看完没收获算我输

专注于Java领域优质技术,欢迎关注作者: tobe 来自:tobe的呓语上一篇介绍 TCP 的文章「TCP 三次握手,四次挥手和一些细节」反馈还不错,还是蛮开心的,这次接着讲一讲关于超时和重传那一部...

用 Wireshark 让你看见 TCP 到底是什么样

前言当你看到这篇文章时,你只能看到已经渲染好的文字和图像,而网络数据的交互对我们来说,却是看不见的,所以学习计算机网络原理的时候就会觉得非常的抽象,这一度让我苦恼。而且网络数据交换真实的模样,到底是不...