作者: tobe 来自:tobe的呓语
我们都知道 TCP 协议具有重传机制,也就是说,如果发送方认为发生了丢包现象,就重发这些数据包 。很显然,我们需要一个方法来「猜测」是否发生了丢包 。最简单的想法就是,接收方每收到一个包,就向发送方返回一个 ACK,表示自己已经收到了这段数据,反过来,如果发送方一段时间内没有收到 ACK,就知道很可能是数据包丢失了,紧接着就重发该数据包,直到收到 ACK 为止 。
你可能注意到我用的是「猜测」,因为即使是超时了,这个数据包也可能并没有丢,它只是绕了一条远路,来的很晚而已 。毕竟 TCP 协议是位于传输层的协议,不可能明确知道数据链路层和物理层发生了什么 。但这并不妨碍我们的超时重传机制,因为接收方会自动忽略重复的包 。
超时和重传的概念其实就是这么简单,但内部的细节却是很多,我们最先想到的一个问题就是,到底多长时间才能算超时呢?
超时是怎么确定的?一刀切的办法就是,我直接把超时时间设成一个固定值,比如说 200ms,但这样肯定是有问题的,我们的电脑和很多服务器都有交互,这些服务器位于天南海北,国内国外,延迟差异巨大,打个比方:
- 我的个人博客搭在国内,延迟大概 30ms,也就是说正常情况下的数据包,60ms 左右就已经能收到 ACK 了,但是按照我们的方法,200ms 才能确定丢包(正常可能是 90 到 120 ms),这效率实在是有点低 。
- 假设你访问某国外网站,延迟有 130 ms,这就麻烦了,正常的数据包都可能被认为是超时,导致大量数据包被重发,可以想象,重发的数据包也很容易被误判为超时 。。。雪崩效应的感觉
在这里先引入两个概念:
- RTT(Round Trip Time):往返时延,也就是**数据包从发出去到收到对应 ACK 的时间 。**RTT 是针对连接的,每一个连接都有各自独立的 RTT 。
- RTO(Retransmission Time Out):重传超时,也就是前面说的超时时间 。
Measure the elapsed time between sending a data octet with a particular sequence number and receiving an acknowledgment that covers that sequence number(segments sent do not have to match segments received). This measured elapsed time is the Round Trip Time (RTT).经典方法
最初的规范「RFC0793」采用了下面的公式来得到平滑的 RTT 估计值(称作 SRTT):
SRTT <- α·SRTT +(1 - α)·RTT
RTT 是指最新的样本值,这种估算方法叫做「指数加权移动平均」,名字听起来比较高大上,但整个公式比较好理解,就是利用现存的 SRTT 值和最新测量到的 RTT 值取一个加权平均 。
有了 SRTT,就该设置对应的 RTO 的值了,「RFC0793」是这么算的:
RTO = min(ubound, max(lbound, (SRTT)·β))
这里面的 ubound 是 RTO 的上边界,lbound 为 RTO 的下边界,β 称为时延离散因子,推荐值为 1.3 ~ 2.0 。这个计算公式就是将 (SRTT)·β 的值作为 RTO,只不过另外限制了 RTO 的上下限 。
这个计算方法,初看是没有什么问题(至少我是这么感觉的),但是实际应用起来,有两个缺陷:
There were two known problems with the RTO calculations specified in RFC-793. First, the accurate measurement of RTTs is difficult when there are retransmissions. Second, the algorithm to compute the smoothed round-trip time is inadequate [TCP:7], because it incorrectly assumed that the variance in RTT values would be small and constant. These problems were solved by Karn's and Jacobson's algorithm, respectively.这段话摘自「RFC1122」,我来解释一下:
- 当出现数据包重传的情况下,RTT 的计算就会很“麻烦”,我画了张图来说明这些情况:图上列了两种情况,这两种情况下计算 RTT 的方法是不一样的(这就是所谓的重传二义性):但是对于客户端来说,它不知道发生了哪种情况,选错情况的结果就是 RTT 偏大/偏小,影响到 RTO 的计算 。(最简单粗暴的解决方法就是忽略有重传的数据包,只计算那些没重传过的,但这样会导致其他问题 。。详见 Karn's algorithm)
- 情况一:RTT = t2 - t0
- 情况二:RTT = t2 - t1
- 另一个问题是,这个算法假设 RTT 波动比较小,因为这个加权平均的算法又叫低通滤波器,对突然的网络波动不敏感 。如果网络时延突然增大导致实际 RTT 值远大于估计值,会导致不必要的重传,增大网络负担 。( RTT 增大已经表明网络出现了过载,这些不必要的重传会进一步加重网络负担) 。
推荐阅读
- 小苏打的学名叫什么名字 小苏打的学名叫什么
- 赖阳,从茶的角度看老字号当代生存之道
- 身份证号码各数字含义
- MySQL 数据库、表、字段的命名建议规范
- 图文详解为什么电脑没有声音
- 做梦喊一个人的名字但不认识这人 做梦喊一个人的名字怎么办
- 湘字号集体亮相中博会 怡清源为接待专用茶
- Java 字符串常量池介绍,String Pool 的实现
- MySQL常用的字符型数据
- 详解Web应用的底层逻辑,掌握Spring框架开发的思路