欢迎去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. 100个命令Linux常用命令大全

    Linux常用命令大全100条: 1,echo "aa" > test.txt 和 echo "bb" >> test.txt//>将原 ...

  2. Jquery入门(初学者易懂)

    一.定义 jQuery是一个快速.简洁的JavaScript框架,是继Prototype之后又一个优秀的JavaScript代码库(或JavaScript框架).jQuery设计的宗旨是"w ...

  3. Linux目录结构和基础知识

    目录结构: /bin:存放系统常用的命令程序 /boot:系统启动或引导所需要的一些文件 /dev:可用的设备文件 /etc:系统配置相关的东西 /home:所有用户的主目录 /lib,lib64:存 ...

  4. 原生拖拽js利用localstorage保存位置

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. 利用sfc文件构建网络渗透

      收集哈希 SCF(Shell命令文件)文件可用于执行一组有限的操作,例如显示Windows桌面或打开Windows资源管理器,这并不是什么新鲜事.然而,一个SCF文件可以用来访问一个特定的UNC路 ...

  6. poj 2681 字符串

    http://poj.org/problem?id=2681 给你任意长度的字符串,找出两串字符中不相同的字符个数(总数) #include<string> #include<cst ...

  7. 大数据 --> 分布式服务框架Zookeeper

    分布式服务框架 Zookeeper Zookeeper系列 分布式服务框架 Zookeeper -- 管理分布式环境中的数据

  8. web 表单提交按钮的测试点

    web表单中的提交按钮的测试点: 在提交前需要理解清楚的点: 1.表单中哪些字段是必填项 2.表单中字段内容的限制:非空.重复.长度.特殊字符,空格.以及一些和业务相关的约束条件 测试点: 1.是否支 ...

  9. 使用 Except 和 Intersect

    做了一个如下的小厕所,如果我需要得到返回是 d,f 那我需要用那组语句呢? A: ;WITH CA AS( SELECT * FROM (VALUES('a'),('b'),('c'),('d'))a ...

  10. JavaWeb学习笔记总结 目录篇

    JavaWeb学习笔记一: XML解析 JavaWeb学习笔记二 Http协议和Tomcat服务器 JavaWeb学习笔记三 Servlet JavaWeb学习笔记四 request&resp ...