聊聊TCP的重传退避与公平

1次阅读

共计 1375 个字符,预计需要花费 4 分钟才能阅读完成。

TCP在发送报文后,如果没有收到对端应答,那么在重传定时器超时后会触发重传,超时时间遵循二进制退避原则,也就是 {1,2,4,8,16} 这样成倍地扩大超时时间。退避是因为 TCP 认为丢包意味着网络有拥塞,为了不加重网络的拥塞,TCP选择等待 更长 的时间再进行重传。这和 CSMA/CD 中的二进制退避算法如出一辙。

在链接曾经提到过网络拥塞的来历,网络中的网络设备 (路由器、交换机) 在收到了超过队列限制的报文后,后续的报文会被丢弃。从 TCP 采用的二进制退避算来看,TCP绝对算得上是网络里的 谦谦君子 了,它信守的规则是:既然已经堵了,我就等一会儿再发,如果还堵,我就再多等翻倍的时间

对整个网络来说,这的确是减轻负担的好办法。要知道,在发送窗口已满的情况下,指数退避一次,意味着单位时间内发送的报文变成了原来的 1/2, 再退避一次,就只有原来的1/4!就像是汽车限号出行,单双号限行不好用,我就规定一辆车只能在4 天中开 1 天 …

But, TCP 真的需要如此克制自己吗?,换个说法,为什么 TCP 一定要 x2 退避?难道重传定时器超时时间不能线性增大 (每次增加X 秒),或者乘以一个更小的系数 (比如x1.5)?我们可以从CSMA/CD 找到灵感。

CSMA/CD使用 x2 的原因很好理解,共享介质中的每个节点并不知道其他还有多少节点,使用 x2 退避就是利用二分法快速找到让整个系统稳定运行的时隙分配方案!

举个例子,假设系统中有 4 个节点发包速率相同,那么最终的稳定分配方案自然就是将一段时间分为 4 份,每个节点占用 1 个时隙。如果此时又加入 4 个相同的节点。那么显然,所有的 8 个节点都会发包冲突。如何才能不冲突呢?当然是分配给每个节点的时间再减小一半!当然这里举的例子节点都是 2 的整数幂。如果不是呢?此时 2 进制退避依然能很快地达到稳定,也许这个时候的时隙分配方案不是最合理的,但是正如前面说的,每个节点并不知道其他还有多少节点,对单个节点来说,二进制退避是最快速找到让每个节点都正常工作的方案!

二进制退避方案中隐含了对 公平性 的考虑,它是站在整个网络的角度,而不是其中某一台主机!

但对一台特定的主机,不遵守这个退避规则显然好处更多 … 比如使用固定的重传定时器时间。在这种网络中,没有拥塞时大家相安无事,一旦出现了拥塞,那么不退避的主机理论上就能发出更多的报文!

当然,这似乎 牺牲 了其他遵守规则主机的利益,它们的重传次数会增加。那么,如果大家都不遵守呢?结果就是大家的重传次数都增加了,拥塞甚至比大家都遵守还要糟糕,因为网络上的设备丢的包更多了!

这就有点 囚徒困境 的味道了,如果别人退避你不退避,你能获利,如果别人比退避而你退避,那么你就吃亏了,如果大家都不遵守,那么比都遵守还要糟糕 ……

当然,现实中的网络并不同于 CSMA/CD 中的共享介质,一个简单的区别就是,在 CSMA/CD 中,如果一个节点监听到信道繁忙,它是不是发送数据的,它需要等待;而在网络中,并不存在这样的共享介质,并且路由器是又缓冲队列的,因此两个主机还是可以都发送报文。

话虽如此,很多游戏为了保证实时性不会选择 TCP!毕竟,TCP 太无私了,它是为了整个网络考虑的。而实时游戏需要的是什么!快速!那么如果它们有需要可靠传输怎么办?很简单,使用 UDP,然后在用户态自己做ARQ 就好了,想怎么折腾怎么折腾,比如 KCP 就是这么个东西。

正文完
 0