{TCP/IP细节}糊涂窗口综合症

之前的一篇文章,发在blogjava上,可惜那儿人气太低,冷冷清清,弄得毫无写作的欲望,javaeye还是热闹点,转发到这里来.

早期的TCP实现,本来是为了 提高传输效率&&控制传输速率 而存在的滑动窗口[Sliding window] 机制,在发送方和接收方 生产和消耗数据的速率差异较大时,会产生一种比较犯傻的传输状态:发送方发送的数据报文[Segment] 具有一个大大的头部,附带的数据却少得可怜.

犯傻症的产生

考虑以下两种情况:
    1.接收方的应用程序读取数据老慢老慢,一次只1byte.
             可以想见,很快,接收方的缓存被填满,于是回发窗口通告[window advtisement]说:已满,勿发.
             接下来,应用程序慢吞吞的读入1byte,缓存空余出来一字节,兴高采烈的再次回发窗口通告说:一字节可用,快来填满我.
             发送方接到通告,生成并传送数据部分只一字节的报文.
             然后,如此重复.

       很明显,这样不好,效率太低. 一般TCP报文头部大小最少也是20字节,结果只拖这么点数据过去,头部的处理(校验和的计算,流的序列号的读取,数据的提取),到IP数据报的封装及相应的解封 又要照行不误,实在效率忒也底下了.

    2.相应的是发送方的应用程序,产出数据慢,一次一字节,一字节一个报文,也会导致效率问题.

犯傻症的防治
   1.接收方
         a.对每一个到达的TCP报文回送ACK,但在窗口的空余增大到指定大小之前,我都不会报告窗口的大小,免得回送一个1byte,你就想也不想就发个报文过来.
         b.或者,延迟ACK的发送.并不一定对每个TCP报文都回送ACK,而是每个报文到达后,稍等片刻.
        
 Adventage:
             1.减少交通流量.一个ACK可能对多个报文做出确认回复,不用一一回复啦.
             2.Piggyback. 当接收方收到数据后需要回送数据时,可以在回送数据的TCP报文中捎带[piggyback] ACK确认,同样减少了网络流量.
             3. 窗口通告[window advertizement]是在数据于缓冲区被读取后才发出的,这和对应的报文的到达时间有一定延后,而ACK的延迟发送就可能等待到窗口大小的改变,两者一起回发(又是piggyback?),同样减少了数据流量.
         
Disadventage:
             1.报文重传时间是根据报文来回时间推测而来的.ACK延迟策略影响了对合理重传时间的判断,本来一秒钟就到了的,结果延迟设定的时间太夸张,用了十秒,这边的传送方就说那好吧,就15秒吧,15秒还不到我就算你报文丢失了,直接结果就是反应迟钝,正常的5秒钟就可以判定丢失的,偏要等到15秒,好难等啊...
             2.另外,延迟时间太夸张了,最直接的结果就是发送方等啊等,没等到,干脆重传了(其实那报文早到了,没回送确认而已),这样添加了无谓的重传,增加网络负担.
       
  Solve:
             相应的解决方法简单得很,既然都是延迟时间太夸张导致的,我就把延迟时间设个上限呗,现在的建议是0.5秒.
    

    2.发送方                    
        想象一下,为了避免传输小数据报,可以让TCP稍等一会儿,待到数据聚集多后,一起发送,嗯,想法不错,不过等多少时间呢?
        0.5秒?50秒?
        关键是TCP应对的对象是多种多样滴,可以是人,是打字快的人,是半小时打一个字的打字慢的人,是5M/S的文件传输.....
        对人,0.5秒太短,对文件传输,时间又太长.故时间的选择是一个问题.

        想一种自适应的策略出来.

        1.对打字慢的人来讲,不应该强行奢望说每个报文都携带大量数据,不然应用程序延迟太大,聊QQ我打一行字不一定有20byte呢,结果你强行说100byte我再给你发,我不是很郁闷.所以这种情况理想下应该及时发送,相比人的慢速,网络的ACK是到的快的,所以但凡ACK到达,我就发送下一数据报,而不理睬是否报文数据达到一定大小(这样就没有提高传输效率一说了,不过程序嘛,当然用户体验第一啦,小牺牲一下效率也是值得滴).
        2.对传输大户比如文件传输呢?
        还发一个报文,乖乖等ACK然后再发下一个就太慢了吧,乖乖,我可是10M/S的速度写入啊,这时就应该藐视ACK,只要缓冲区数据写入填满了最大报文,就立刻发送,毫不等待(当然了,还是得遵循滑动窗口的限制吧,猜测,待确认).
        而兼容以上两种情况的策略就诞生了:

       1 . 对从应用程序收到的第一块数据,发送端的TCP直接将它发送出去,哪怕只有一个字节。

       2 .此后,发送端的TCP就在输出缓存中积累数据,并等待:或者接收端的TCP发送出一个确认,或者数据已积累到可以装成一个最大的报文段。二者满足其一,发送端的TCP就可以发送这个报文段。

       3.对剩下的传输,重复步骤2。这就是:如果收到了对报文段x的确认,或者数据已积累到可以装成一个最大的报文段,那么就发送下一个报文段(x + 1)

   (我看的《Intenetworking with TCP/IP》中,作者评价这个策略是Surpring and Elegant.赞赏之情溢于言表)

.

猜你喜欢

转载自beefcow.iteye.com/blog/686658
今日推荐