TCP流量控制原理
TCP采用个各种办法来减少流量的传输量以及信道的利用率。
延时确认-减少传输数量
TCP允许延迟一会再发送ACK,这样可以将ACK和相同方向的数据结合起来进行发送,从而降低ACK的数量,在一定程度上减轻网络负载。
图中通过延迟ACK减少了一个ACK的传输数量
也叫稍带确认(piggybacking)。一般处理请求并产生应答的时间小于200ms时发生。如果耗时比较长,一般是先确认后应答(发数据)
滑动窗口
滑动窗口的工作原理在之前已经做了详细的介绍,在滑动窗口中有一个很重要的概念:窗口大小。问题来了,TCP是如何设置窗口大小的呢?在介绍之前先看一下TCP的缓存结构。
对于TCP来说接收和发送方都维护着一个缓存。看上图会发现个问题:如果写进程写的速度大于读进程的读取速度,这样会导致接收缓存溢出,最终导致发送失败。所以引入了窗口大小的概念,接收方通过ACK来实时告知发送方自己剩余缓存的大小(即自己还能接收多少数据),这个大小就是滑动窗口的大小,在传输的过程中,每一次ack接收方会根据自己的缓存大小动态的调整窗口的大小,下图详细展示了这一过程。
0窗口(Zero Window)
上面滑动窗口的展示中最终发送窗口变成了0.
当发送方收到窗口为0的参数后,便不再发送数据给接收方,这个时候接收方进程一直在读取数据,最终接收方的TCP缓存会清空,有空间接收数据,这个时候接收方会通过一个ack通知发送方窗口的大小,但是这个ack有可能会丢失(ack没有重传功能),发送方因为窗口时0,一直没有发送数据,所以无法得知最新的窗口大小,通信双方都进入了一直等待状态。
为了解决这个问题,TCP为每一个链接设计了一个持续计数器(persistence timer),当窗口大小为0时,就会启动这个计数器,当计数器到期后会发送一个零窗口(zero window)探测报文段(一个字节),接收方通过确认这个探测报文时可以告知发送方最新的窗口大小。(TCP规定,就算窗口为0,也要接收零窗口探测报文)。下图展示了这种case。
糊涂窗口综合征(Silly Window Syndrome-SWS)
通俗来解释这个场景就是一架可以坐500人的飞机只拉1个乘客和5个机组人员,资源极大的浪费。
以太网最大传输单元(MTU:max transmission unit)为1500字节,TCP+IP的头部字段为40字节,所以TCP的最大报文(MSS:Max segment size)长度为1460字节。当传递的报文的大小远远小于1460。尤其小于头部40的时候,就出现了上面说的一个大飞机拉一个人的情况,其中MTU为飞机的容量(500),机组乘员为TCP/IP头部(5),传输报文为乘客(1)。
发送端引起的SWS
发送窗口的size大于MSS,但是需要发送的数据远远小于MSS,这种情况的解决采用nagle算法:
if there is new data to send #有数据要发送
# 发送窗口缓冲区和队列数据 >=mss,队列数据(available data)为原有的队列数据加上新到来的数据
# 也就是说缓冲区数据超过mss大小,nagle算法尽可能发送足够大的数据包
if the window size >= MSS and available data is >= MSS
send complete MSS segment now # 立即发送
else
if there is unconfirmed data still in the pipe # 前一次发送的包没有收到ack
# 将该包数据放入队列中,直到收到一个ack再发送缓冲区数据
enqueue data in the buffer until an acknowledge is received
else
send data immediately # 立即发送
end if
end if
end if
else里面有小块数据(小于MSS)时候,解释下:这个时候需要看是否还有已经发送待确认的数据,如果有则等着先不发,等于先在发送端攒数据。某种程度上又变成了停等协议,如果ACK返回的比较慢,小数据等待的时间就会比较长,最终会影响性能。有些情况并不适用nagle算法,比如人机交互的游戏等,可以禁用nagle算法。
接收端引起的SWS
对于接收端来说,如果接收缓存的窗口大小小于MSS或者某个给定的值后,直接ack 接收窗口的size为0。这样当发送方收到接收窗口为0时,则停止发送数据进行等待,在此期间,接收端的应用一直在读取数据,这样接收窗口会慢慢变大,最终通过发送方的0窗口探测请求告知发送方最新的窗口大小,从而再次开始数据传输。
以上介绍了TCP流量控制的一个整体过程,全文完。