目录
流量控制
滑动窗口
滑动窗口主要用于进行流量控制,举个例子,A向B不停地发送数据,如果A发送数据速度比B接收数据速度快,那么最终就会出现B的接收缓存满的情况,此时对于A发送并溢出的数据,B是无法做出ACK应答的,而A收不到ACK应答就会继续重发,这样一来二去A就会发很多没有必要的报文。引起这一问题的原因,主要就是A并不知道B能接收多少数据,从而发送了很多B无法接收到的数据,而滑动窗口则解决了这一问题。
滑动窗口是通过TCP报文中的“窗口大小”win字段实现的,对于B来说,“窗口大小”反映了B下一次还能接收多少数据(字节为单位),如此,B在回发给A的ACK报文中,携带的确认序号指定了B下一次期望接收的数据包序号,同时该ACK报文中也包含了B的窗口大小。A在收到B的ACK报文应答后,可以知道B的窗口大小以及下一次应该发的报文序号,根据这两条信息就可以确定A的发送窗口(如果只考虑流量控制的话,那么发送窗口大小就等于接收窗口大小),而发送窗口内的数据就是下一次应该发送的数据。
滑动窗口为0的情况
如果A仍然一直发送数据,并且发送得很快,而B读取数据很慢,最终就会造成B窗口大小为0的情况。一旦B的窗口为0,那么B回发回来的ACK报文中就会显示win=0,此时A接收到了这条ACK报文,就知道B不能再接收数据,就不再给B发送数据了。
而这种情况往往不会持续很久,当B从缓存区中读取了一些数据后,B的数据接收缓存中又有了一些存储空间,也就表示B又可以再接收数据了,但是此时A不知道啊!那该怎么去告诉A可以向B发送数据了呢?一种方法是在B的接收缓存有空间时,B直接向A发送ACK报文告诉A可以发送了,需要注意的是,A接收到这条ACK报文后是不需要发送确认的,B只管发出这条报文就坐等A再发数据过来,如果这条报文丢失,A也就不知道此时B可以接收数据,也一直在等待B,双方互相等待,最终就会造成死锁。
为了解决这一问题,TCP为每一个连接都设有一个持续计时器(persistant timer)。
当A收到B的零窗口通知(win=0),A就会启动这个持续计时器。当持续计时器超时,A就会向B发送一个探测报文段(仅携带一个字节数据)(这条报文并非是确认报文,如果收不到ACK确认会重传),而B收到这个探测报文段,就会再回发一个ACK报文,这条ACK报文中给出了B当前的窗口大小。A收到了这条报文后,如果发现窗口大小仍然为0,那么A就会重置持续计时器重新开始计时;如果窗口大小不为0,那么就会恢复正常状态向B发送数据。
糊涂窗口综合征
通过滑动窗口,的确可以实现A与B端到端的数据传输流量控制,但是也会有其他情况:当B的数据缓存满了之后,如果B的数据处理速度足够慢,一次只能处理少许字节如1个字节,那么此时窗口大小就为1,A在探测到窗口大小为1后,就会向B发送一个字节的数据包,而一个数据包上只发送一个字节,可见是非常浪费的,如果B继续只处理一个字节,那么A就会一个字节一个数据包发出来,这样的话,在网络上就会有很多A的“小包”,这就是“糊涂窗口综合征”,增大了网络传输的负担,降低了网络效率。
发送端解决“糊涂窗口”——Nagle算法
为了解决这一问题,从发送方来说,最直接的办法就是避免发送“小包”,这就是Nagle算法的作用。
Nagle算法的核心就是:如果发送缓存区的是“小包”那就延迟发送,如果是“大包”那就直接发送。
在发送缓存中&