欢迎去gitbook(https://www.gitbook.com/@rogerzhu/)看到完整版。

如果对和程序员有关的计算机网络知识,和对计算机网络方面的编程有兴趣,虽然说现在这种“看不见”的东西真正能在实用中遇到的机会不多,但是我始终觉得无论计算机的语言,热点方向怎么变化,作为一个程序员,很多基本的知识都应该有所了解。而当时在网上搜索资料的时候,这方面的资料真的是少的可怜,所以,我有幸前两年接触了这方面的知识,我觉得我应该把我知道的记录下来,虽然写的不一定很好,但是希望能给需要帮助的人多个参考。我的计划是用半年时间来写完这一系列文章,这个标题也是我对太多速成文章的一种态度,好了,废话不再多扯了,下面是其中的一节内容,更多内容可以去gitbook上找到。

在TCP中,超时重传机制是和应答确认机制一样组成TCP可靠传输的关键设计。而超时重传机制中最最重要的就是超时计时器的时间选择的了,很明显,在工程上,在数据发送的过程中,如果用一个固定的值一直作为超时计时器的时长是非常不经济也非常不准确的方法,所以这一篇就来说说TCP中的超时计时器的设计哲学。

太短不行,太长也不行

超时超时,首先你得定义什么是正常的时间,才能知道有没有超过正常的时间。先假设一个非常理想的环境,这个环境理想到和以前很多物理题一样,不考虑摩擦力。我们假设网络很通畅且速率稳定,而且处理包的速度忽略不计,这样一个包发送到对端的时间永远都是一样的,将这个时间记为t。那么很明显,如果超过两倍的t还没有收到对端的回复,我们就可以肯定超时了。所以在这种情况下超时计时器只要设置的比两倍t长就行了。只要过了这个时间,发送端就会重新发送这个包。

那么这个时间是不是越长越好呢?答案很明显不是,因为太长会人为的减少通信的速率,对于通信这种有时候一点点速率的提高都让人欣喜若狂了,如果你还人为的浪费时间那真是暴殄天物了。

那么如果这个时间设置的太短会怎样呢?在这个理想情况下就是小于2倍的t,这会导致太多不必要的重传。也许ACK正在路上,你却错误的认为是丢失了,那么网络中就会增加很多本来就不必要的包。

而且,要知道,现实的网络环境是十分复杂多变的,有时候可能突然的抽风,有时候可能突然的又很顺畅,所以说如果只用一个一直不变的时间作为重传计时器的时长是完全不现实,不可用的。所以很多计算重传时间的算法就被设计出来。

调的一手好参数

TCP把一个包从发送端发送出去到接收到这个包的回复这段时间称之为RTT,学名round-trip time。如果在一个包发送出去以后,超过了RTT还没有接受到回复确认,那么很明显,这个包超时了。如果你还记得前面的关于的PING那一篇,里面就有一个time指示了来回时间,但是这个是ICMP的来回时间,和TCP的这个RTT是完全不一样的概念。通过wireshark你可以看到每一次的RTT的值。

这个RTT的计算很简单,只要把收到确认包的时间减去发送包的时间就得到了这个答案。

现在开始对于重传计时的第一次思考,上面说了这样一个来回就说明包是成功的接收了并且没有发生任何异常,那么可不可以简单的用这个值作为标准来作为判断超时的依据呢?也就是如果超过了0.285s没有收到ack就开始重传,很明显,不能。原因是这个RTT是过去完成时了,是上一次成功的时候的时间,和下一次网络会不会突然抽风,还是会突然变得更通畅没有太大的必然关系,最愚蠢的一种思维就是简单的用过去代表未来。但是数学给我们提供了一种用已知大概去逼近未知的方法,那就是用概率统计的思维。所以最简单的一个办法是用过去的几次平均值来作为这一次重传计时器的时长,毕竟这是初中学过的理论。不过这个方法明显太过于幼稚,缺乏灵活的控制,所以说,第一次设计的尝试就出现了。

为了能够用更加灵活的方法来估算出重传时间,一个叫SRTT的概念被引进,SRTT学名是Smoothed RTT。估算重传时间(以后称之为RTO,Retransmission Timeout)的算法如下:

SRTT = (α * SRTT) + ( (1 -α) *RTT)

其中这个奇妙莫测的阿尔法取值在0.8到0.9之间,为什么这样取,我也不知道,我至今也没有找到原因。对于这第一个公式,具体实际中的做法是这样的,首先采样几次RTT的值,然后在第一次迭代的时候SRTT的初始值为RTT,后面就是根据每次计算出来的SRTT来计算就行了。这个公式有个你应该比较熟悉的中文名字,叫做加权移动平均。

在计算出SRTT之后,就使用这个值来计算我们需要的RTO,其方法如下:

RTO = min[UBOUND, max[LBOUND,(β * SRTT)]]

这其中UBOUND是一个上限时间,比如1分钟,LBOUND是一个下限时间,比如1秒钟,β,哈哈,又是一个神奇的参数,取值在1.3到2.0之间,叫做延迟方差因子,到底取啥,为什么取这个值,我,还是不知道。

这个方法有什么问题呢?问题就在这个RTT的计算上,前面说过RTT的计算是接收到ACK的时间和包发送出去的时间的差值,在正常情况下还好,如果是在采样的过程中发生了重传,那么到减去的时间是第一次发送的时间还是重传发送的时间呢?

如果是减去第一次发送的时间,那么很明显,这个RTT计算大了。那你可能会说了,从直观上说,用第二次发送的时间计算才是合理的。但是有一种情况,假设本来应该到达的ACK不是丢失了,只是延迟到达了,也就是说你刚重传,这个迷路的ACK就到了,那么你用这个时间减去第二次发送的时间,明显就小了。

这个时候两个叫Phil Karn和Craig Partridge的人就针对这个问题提出了一个算法,其解决方案十分简单,既然重传情况这么复杂,那么在采用RTT的时候直接忽略重传不就行了。你先收起你的吐槽说尼玛这样我要早出生几年也能想出这个办法啊,人家论文里还写了很多其他的东西,这个只是其中之一,而且这个算法也有很大的问题,Karn针对这个问题还提出了一个可行的解决方案,至少在工程上有了个可行的路子。

这个问题是什么呢?假设在某个时间,网络极度的抽风,突然由快变得很慢,导致所有的包都要重传。这下好了,因为前面一直很通畅,所以必然RTO很小,那么你又说重传的包不参与RTT的采样,这下完了,RTO永远不会更新,只会不断的重传,情况会越来越糟。而Karn针对这个提出了一个解决方案,只要重传,那么RTO就翻倍,这样就保证了在极端情况下不会导致越来越糟。

Karn的算法解决了初代算法的问题并且有了个可行的方案,但是RTO粗暴翻倍的做法感觉还是比较浪费。所以,在一年之后又有两个人Jacobson 和 Karels 针对这种加权移动平均的算法对RTT波动handle能力不强的弊端做了修正。其原理是用最新采样的RTT和平滑过的SRTT的差距来作为另一个影响因子。

SRTT = SRTT + α * (RTT - SRTT)

DevRTT = (1-β) * DevRTT + β *(|RTT - SRTT|)

RTO = μ * SRTT + δ * DevRTT

这三个公式就是现在TCP协议中真正运用的算法,关于这些参数,α是取0.125,β是0.25,μ 是1,δ是4,这就是linux中的取值,至于为什么,没有人知道,但是在实际效果中,果真就很有效,在编程过程中,我们称这种玄学叫做调的一手好参数。

三十天学不会TCP,UDP/IP网络编程 -- RTT的计算的更多相关文章

  1. 三十天学不会TCP,UDP/IP网络编程-IP头格式祥述

    我又来了,这篇文章还是来做(da)推(guang)介(gao)我自己的!俗话说事不过三,我觉得我下次得换个说法了,不然估计要被厌恶了,但是我是好心呐,一定要相信我纯洁的眼神.由于这两年接触到了比较多的 ...

  2. 三十天学不会TCP,UDP/IP网络编程-ARP -- 连接MAC和IP

    继续来做(da)推(guang)介(gao)我自己的!由于这两年接触到了比较多的这方面的知识,不想忘了,我决定把他们记录下来,所以决定在GitBook用半年时间上面写下来,这是目前写的一节,目前已完成 ...

  3. 三十天学不会TCP,UDP/IP网络编程-TraceRoute的哲学

    新年快乐,继续来部分粘贴复制我的这一系列文章啦,如果对和程序员有关的计算机网络知识,和对计算机网络方面的编程有兴趣,欢迎去gitbook(https://www.gitbook.com/@rogerz ...

  4. 三十天学不会TCP,UDP/IP网络编程 - 绅士的开始

    经过了过年的忙碌和年初的懈怠一切的日子,我又开始重新更新了~这是最新的一篇~完整版可以去gitbook(https://www.gitbook.com/@rogerzhu/)看到. 如果对和程序员有关 ...

  5. 三十天学不会TCP,UDP/IP网络编程-UDP,从简单的开始

    如果对和程序员有关的计算机网络知识,和对计算机网络方面的编程有兴趣,欢迎去gitbook(https://www.gitbook.com/@rogerzhu/)star我的这一系列文章,虽然说现在这种 ...

  6. 三十天学不会TCP,UDP/IP网络编程 - RST的用法

    不知不觉也写了这么多了,继续我的自己的推广大业~完整版可以去gitbook(https://www.gitbook.com/@rogerzhu/)看到. 如果对和程序员有关的计算机网络知识,和对计算机 ...

  7. 三十天学不会TCP,UDP/IP网络编程 - UDP的实践--DHCP

    在经历了一顿忙碌加出去玩了玩之后,我又开始重新更新了~这是最新的一篇~完整版可以去gitbook(https://www.gitbook.com/@rogerzhu/)看到,在gitbook的后台流量 ...

  8. 三十天学不会TCP,UDP/IP网络编程 -- TCP中的智慧之连续ARQ

    突然发现上一篇文章贴图有问题,关键我怎么调也调不好,为了表达歉意,我再贴一篇gitbook上的吧,虽然违背了我自己的隔一篇在这里发一次的潜规则~其余完整版可以去gitbook(https://www. ...

  9. 三十天学不会TCP,UDP/IP编程--MAC地址和数据链路层

    这篇文章主要是来做(da)推(guang)介(gao)的!由于这两年接触到了比较多的这方面的知识,不想忘了,我决定把他们记录下来,所以决定在GitBook用半年时间上面写下来,这是目前写的一节,后面会 ...

随机推荐

  1. Selenium学习资源和网站

    用于收集常用的网站和学习资源: 文章: Selenium私房菜系列--总章 WEB 自动化测试工具 Selenium 简介及其应用 Selenium教程 和我一起学 Selenium WebDrive ...

  2. [C#]基于命名管道的一对多进程间通讯

    在工作中碰到了一个进程间通讯的问题,大概是这样的: 项目本身是.net Core做的,但是有部分功能Core中不方便实现,有的是依赖Framework,有的是因为权限和安全问题. 那基于这个问题,问了 ...

  3. 关于Unity的协程

    协程 认识协程 //协程不是多线程:是一段在主程序之外执行的代码 //协程不受生命周影响 //作用:能够口直代码在特定的时间执行. //1,延时操作 //2,等待某代码执行结束之后执行 /* 特点:1 ...

  4. IPFS: NAT traversal(NAT穿越)

    IPFS是一个p2p网络,那么一定绕不开的一个问题就是NAT穿越.之前的文章里面也提到过IPFS网络连通性使用的ICE NAT穿越框架,本文简单介绍一下什么是NAT.   为什么有NAT技术? NAT ...

  5. Spring容器中Bean的生命周期

  6. react 实用的性能优化方式

    react 组件渲染分为初始化渲染和更新渲染,当我们更新某个组件的时候,只是想关键路径上组件的render,但react的默认做法是调用所以组件的reder,再生成虚拟dom进行对比,如不变则不进行更 ...

  7. xml解析多个结点方法(C#)

    解析多个结点的XML文件,格式如下: <?xml version="1.0" encoding="utf-8"?> <response> ...

  8. javaAPI中的常用 类 以及接口

    java.lang包中的常用类以及接口 类 1. Integer :Integer 类在对象中包装了一个基本类型 int 的值.Integer 类型的对象包含一个 int 类型的字段. 2. Math ...

  9. Python变量赋值的秘密

    在Python中,我们令一个变量等于另外一个变量时,并不是把值传递给它,而是直接把指向的地址更改了.我们想要查看一个变量在内存中的地址,可以通过id(变量) 来查看.我们通过一个小例子来看看这个有趣的 ...

  10. 配置 CSV Data Set Config 来参数化新增客户信息操作

    1.首先根据新增客户信息的http请求,来确定需要参数化的变量,选取符合测试需求且经常变化或未来会变化的变量为需要参数化的变量,如本文中的客户端名称(sys_name).描述(description) ...