详解TCP:顺序和丢包问题
如果第二个报文段先于第一个到B,也就是包的顺序出错。TCP RFC中并没有详细描述对这种问题的处理,一般是工程师自己来解决。一般有两种方案:1)B立即丢弃失序报文段 2)B保留这个报文段,并等待缺少的字节来填补该间隔,第二种是常用的方法。
为了记录所有发送的包和就收的包,TCP发送端和接收端都用缓存来保存这些记录。发送端的缓存里是按照包的 ID 一个个排列根据处理的情况分成四个部分:
- 发送了并且已经确认的
- 发送了并且尚未确认的
- 没有发送,但是已经等待发送的
- 没有发送,并且暂时还不会发送的
为什么没有发送的要分为两种呢?这里就涉及到流量控制了。流量控制是一个 速度匹配服务,即发送方的发送速率与接收方应用程序的读取速率相匹配。 在 TCP 里,接收端会给发送端报一个窗口的大小,叫Advertised window。这个窗口的大小应该等于上面的2、3之和,就是已经交代了没做完的加上马上要交代的。超过这个窗口大小的,接收端做不过来,就不能发送了。发送端的数据结构如下:
对于接收端来讲,它的缓存里记录的内容要简单一些。第一部分:接受并且确认过的;第二部分:还没接收,但是马上就能接收的;第三部分:还没接收,也没法接收的。对应的数据结构为:
LastByteRead 之后是已经接收了,但是还没被应用层读取的;NextByteExpected 是第一部分和第二部分的分界线;MaxRcvBuffer:'大缓存的量;以上面的图为例,在发送端来看,1、2、3 已经发送并确认;4、5、6、7、8、9 都是发送了还没确认; 10、11、12 是还没发出的;13、14、15 是接收方没有空间,不准备发的。 在接收端来看,1、2、3、4、5 是已经完成 ACK,但是没读取的;6、7 是等待接收的;8、9 是已经接收,但是没有 ACK 的。
发送端和接收端当前的状态为:
- 1、2、3 没有问题,双方达成了一致。
- 4、5 接收方说 ACK 了,但是发送方还没收到,有可能丢了,有可能在路上。
- 6、7、8、9 肯定都发了,但是 8、9 已经到了,但是 6、7 没到,出现了乱序,缓存着但是没办法 ACK。
如果过一段时间,5、6、7 都超时了,就会重新发送。接收方发现 5 原来接收过,于是丢弃 5;6 收到了,发送 ACK,要求下一个是 7,7 不幸又丢了。当 7 再次超时又需要重传的时候,TCP 的策略是超时间隔加倍。每当遇到一次超时重传的时候,都会将下一次超时时间间隔设为先前值的两倍。两次超时,就说明网络环境差,不宜频繁反复发送。
参考资料:《趣谈网络协议》刘超
《计算机网络:自顶向下方法》原书第六版 陈鸣译