TCP流量控制

为什么要进行流量的控制

在实际的TCP报文段传输的过程中,可能会出现发送方的发送速率大于接收方的应用程序的读取速率,因此会出现接收缓存被使用完的情况,所以需要流量控制来进行速度匹配工作。TCP的流量控制可以通过滑动窗口协议来实现。

如何进行流量控制

在给发送方返回ACK报文段的时候,我们都会在该报文段中的接收窗口字段中加入接收方的接收缓存当前还可以接收的数据的大小。如果接收窗口为0时,发送方不能再发送数据了。此时,接收方的应用程序会不断的从接收缓存中读取数据,但是由于接收缓存中腾空的空间太小,接收方必须要在缓存中的空间足够多时,才向发送方发送一个额外的确认报文,通知发送方可以继续发送数据了(这里是考虑到传输效率的问题,本文后面会讨论)。

问题考虑:如果额外的确认报文丢失了怎么办?

我们的发送方一直在等接收方的额外的确认报文的到来,如果该报文在传输过程中丢失了,则发送方就会无休止的在等待接收方的通知,从而会进入一个死锁。为了消除这个现象,TCP协议设置了一个:坚持定时器。其工作流程如下:(参考博客:TCP坚持定时器

  • 发送端收到0窗口通告后,就启动坚持定时器,并在定时器溢出的时候向客户端查询窗口是否已经增大。
  • 在定时器未到,就收到非零通告,则关闭该定时器,并发送数据。
  • 若定时器已到,还没有收到非零通告,就发探查报文。
  • 如果探查报文ACK的通告窗口为0,就将坚持定时器的值加倍,TCP的坚持定时器使用1,2,4,8,16……64秒这样的普通指数退避序列来作为每一次的溢出时间,重复1、2、3步,如果通告窗口非零,发送数据,关闭定时器。

传输效率问题

发送端的问题

有这样的一个应用程序(如:telnet应用),它可能一次只产生一个字节的数据。这样我们的发送端可能生成一个41字节的数据报,然后传输该数据报。一个次传输只能传输一个字节的数据,它不但传输效率低下,而且还会带来其他不好的影响如:造成网络拥塞等。

解决方法是:迫使发送端收集数据,然后,发送一个较大的数据块。发送端的TCP要等待多长时间了?如果它等待过长,它就会使整个的过程产生较长的时延。如果它的等待时间不够长,它就可能发送较小的报文段,于是,Nagle找到了一个很好的解决方法,发明了Nagle算法。而他选择的等待时间是一个RTT,即下个ACK来到时。

解决办法:Nagle算法(针对小分组):

  • 若发送应用进程把要发送的数据逐个字节地送到TCP的发送缓存,则发送方就把第一个数据字节先发送出去,把后面到达的数据字节都缓存起 来。当发送方接收对第一个数据字符的确认后,再把发送缓存中的所有数据组装成一个报文段再发送出去,同时继续对随后到达的数据进行缓存。只有在收到对前一 个报文段的确认后才继续发送下一个报文段。当数据到达较快而网络速率较慢时,用这样的方法可明显地减少所用的网络带宽。
  • 如果缓存中的数据字节数达到了发送窗口的1/2或接近最大报文段长度MSS时,立即将它作为一个报文段发送。(防止报文段太长,在IP中被分片传输)

接收端的问题

接收端的TCP可能产生糊涂窗口综合征。接收方的应用程序消耗数据比较慢,当接收缓存满了,发送方会停止发送数据,而当接收端的应用程序从接收缓存中读取一个字节的数据后,接收端的TCP宣布其窗口大小为1字节。那么发送端就会向其发送一个包含一个字节数据的数据报。这样,问题又回到了发送端,每次发送一个字节的效率太低了,但是我接收缓存就只有一个字节的空间啊。所以解决问题的关键还在接收端上。

解决办法:Clark解决方法

Clark解决方法是只要有数据到达就发送确认,但宣布的窗口大小为零后,直到或者缓存空间已能放入具有最大长度的报文段,或者缓存空间的一半已经空了,才会向发送端发送一个特殊的确认报文。

总结

TCP流量控制是通过滑动窗口来实现,最主要的一个步骤就是:坚持定时器和接收端发送的那个特殊的确认报文。在控制流量的同时也同时可以控制程序的传输效率。