您的位置 首页 java

一文带你搞定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);  

文章来源:智云一二三科技

文章标题:一文带你搞定TCP流量控制

文章地址:https://www.zhihuclub.com/179381.shtml

关于作者: 智云科技

热门文章

网站地图