| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 网络协议 -> TCP协议-TCP超时重传机制 -> 正文阅读 |
|
[网络协议]TCP协议-TCP超时重传机制 |
一、前言在TCP通信中,既要保证在网络正常的情况下提供可靠的交付服务,又要保证在网络异常的情况下也提供可靠的交付服务。而TCP的超时重传机制就是解决在网络异常情况下的可靠传输问题的。 二、通过序列号和确认应答提供可靠性在TCP通信中,当发送端的数据到达接收端时,接收端会返回一个已收到消息的通知。这个通知消息叫做确认应答(ACK)。
?2.1 正常数据传输过程?可以看到,TCP通过确认应答机制实现可靠的数据传输服务。 ?2.2 异常数据传输过程当发送端将数据发出后,会等待对端的确认应答。如果有确认应答,说明数据已经成功到达对端。否则,数据可能已经丢失。 在一定时间内没有等到确认应答,发送端会认为数据已经丢失,并进行重发。这样,即使有丢包,仍能保证数据达到对端,实现可靠传输。 发送端未收到确认应答,不一定是数据丢失。也可能是对端已经收到数据,只是返回的确认应答在途中丢失。这种情况也会导致发送端因没有收到确认应答,而认为数据没有到达目的地,从而进行重发。 此外,也有可能是因为一些其他原因导致确认应答延迟到达,在发送端重发数据后才收到。此时,源发送方只要按照重发机制重发数据即可,但是对于接收方来说,它会反复收到相同的数据。而为了对上层应用提供可靠的传输,必须得放弃重复的数据报文段。 每一次传输数据时,TCP报文段的首部都会标明本报文段的起始序列号,以便对方确认。当接收方收到数据后,会去查询数据报文段首部中的序列号和数据的长度,将自己下一步应该接收的起始序号作为确认应答回复给发送方。就这样,通过序列号和确认应答号,TCP就可以实现可靠传输。 ?序列号 序列号是按顺序给TCP报文段的数据部分的每一个字节都标上号码的编号。
?确认号 TCP首部有一个确认字段,该字段实现TCP传输过程中的确认功能,该字段有两个作用: 1)表示确认字段之前的序列号的字节流均已被成功接收。 2)表示期望收到的下一个报文段的序列号,即下一个报文段的数据部分的第一个字节的序号。 三、TCP超时重传超时重传指的是在发送数据报文段后开始计时,到等待确认应答到来的那个时间间隔。如果超过这个时间间隔,仍未收到确认应答,发送端将进行数据重传。这个等待时间称为RTO(Retransmission Time-Out,超时重传时间)。 还有一个时间叫RTT(Round Trip Time,报文段的往返时间),这个时间间隔是指数据报文段发出的时间戳与收到确认应答的时间戳的时间之差。 3.1 超时重传时间的确定超时重传的概念很简单,但是重传时间的选择却是TCP最复杂的问题之一。因为网络环境的不同,时间会有差异。如果把超时重传的时间间隔设置得太短,就会引起很多报文段的不必要的重传,使网络负荷增大;如果把超时重传设置得多长,则又使网络的空闲时间增大,降低了传输效率。 那么,TCP的超时计时器的超时重传时间究竟应设置为多大呢? TCP采用了一种自适应的算法,它记录一个报文段发出的时间戳,以及收到相应的确认应答的时间戳。这两个时间戳之差就是该报文段的往返时间RTT。 算法步骤如下: (1)计算一个加权平均往返时间RTTs(这也称为平滑的往返时间,s 表示 smoothed。因为进行的是加权平均,因此得到的结果更加平滑)。 第1次测量得到的RTT样本值,作为RTTs的初始值。从第2次开始,新的RTTs值使用如下的公式计算得到:
(2)基于计算得到的RTTs时间设置超时重传时间RTO。显然,超时计时器设置的超时重传时间RTO应略大于上面得到的加权平均往返时间RTTs。 RFC 6298 建议使用下式计算RTO:
RFC 6298 建议这样计算RTTd。当第一次测量时,RTTd的值取为测量到的RTT样本值的一半。在以后的测量中,则使用下式计算加权平均的RTTd:
3.2 RTT样本的测量上面所说的RTT样本的测量,实现起来相当复杂。示例如下: 如下图所示,发送方发出一个报文段后,设定的重传时间到了,还没有收到确认应答。于是重传报文段。经过一段时间后,收到了确认报文段。现在的问题是:如何判定此确认报文段是对先发送的报文段的确认还是对后来重传的报文段的确认?由于重传的报文段和原来的报文段完全一样,因此源主机在收到确认后,就无法做出正确的判断,而正确的判断对确定加权平均RTTs的值关系很大。 ?若收到的确认是对重传报文段的确认,但却被源主机当成是对原来的报文段的确认,则这样计算出的RTTs和超时重传时间RTO就会偏大。若后面再发送的报文段又是经过重传后才收到确认报文段,则按照此方法得出的超时重传时间RTO就会越来越长。 同样的道理,若收到的确认是对原来的报文段的确认,但被当成是对重传报文段的确认,则由此计算出的RTTs和RTO都会偏小。这就必然导致报文段过多地重传。这样就有可能使RTO的值越来越短。 根据以上所述,Karn 提出了一个算法:在计算加权平均 RTTs 时,只要报文段重传了,就不采用其往返时间样本。这样得出的加权平均 RTTs 和 RTO就比较准确。 但是,这又可能引起新的问题。设想出现这样的情况:报文段的时延突然增大了很多。因此,在原来得出的重传时间内,不会收到确认报文段。于是就重传报文段。但根据 Karn 算法,不考虑重传的报文段的往返时间样本。这样,超时重传时间就无法更新。 因此需要对 Karn 算法进行修正。方法是:报文段每重传一次,就把超时重传时间RTO增大一些。典型的做法是取新的重传时间为旧的重传时间的 2倍。当不再发生报文段的重传时,再根据上面给出的公式计算超时重传时间RTO的值。实践证明,这种策略较为合理。 总之,Karn 算法能够使TCP区分开有效的和无效的往返时间样本,从而改进了往返时间RTT的估测,使超时重传时间的计算更加合理。 当然,数据不会被无限、反复地重发。当达到一定重发次数后,如果仍然没有任何确认应答返回,就会判断为网络或对端主机发生了异常,TCP模块就会强制关闭连接,并且通知上层应用通信异常强行终止。 3.3 Linux系统中与超时重传相关的内核参数?Linux系统有两个重要的内核参数与TCP超时重传相关:/proc/sys/net/ipv4/tcp_retries1 和 /proc/sys/net/ipv4/tcp_retries2。 ?查看这两个内核参数的默认值:
前者指定在底层IP接管之前TCP最少执行的重传次数。默认值是3。 后者指定连接放弃前TCP最多可以执行的重传次数。默认值是15。(一般对应13~30min)。 四、选择确认SACK问题:如果接收方收到的报文段无差错,只是未按序到达,中间还缺了一些序号的字节数据,那么能否设法只重传缺少的数据而不重传已经正确到达接收方的数据呢?
答案是可以的,选择确认就是一种可行的处理方法。下面我们用一个例子来说明选择确认的工作原理。 当TCP的接收方在接收对方发送过来的数据字节流的序号不连续时,结果就形成了一些不连续的字节块,如下图所示。 可以看出,序号 1 ~ 1000 已收到了,但序号 1001 ~ 1500 没有收到。接下来的字节流又收到了,可是又缺少了 3000 ~ 3500。再后面从 4501 起又没有收到。也就是说,接收方收到了和前面的字节流不连续的两个字节块。如果这些字节块都在接收窗口之内,那么接收方就先收下这些数据,但要把这些信息准确地告诉发送方,使发送方不要再重复发送这些已收到的数据。 从上图可以看出,和前后字节不连续的每一个字节块都有两个边界:左边界和右边界。因此在图中用四个指针标记这些边界。请注意,第一个字节块的左边界 L1=1501,而右边界 R1 = 3001 而不是 3000。这就是说,左边界指出字节块的第一个字节的序号,但右边界减 1 才是字节块的最后一个字节序号。同理,第二个字节块的左边界 L2=3501,而右边界 R2 = 4501。 我们知道,TCP报文段的首部没有哪个字段能够提供上述这些字节块的边界信息。RFC 2018 规定,如果要使用选择确认 SACK 功能,那么在建立TCP连接时,就要在TCP 首部的选项字段(即Options)中加上“允许SACK”的选项,而通信双方必须都事先约定好。如果使用选择确认,那么原来首部中的“确认号”字段的用法仍然不变。只是以后在TCP报文段的首部中的“选项”字段中都增加了SACK选项,以便报告收到不连续的字节块的边界。 由于TCP首部的“选项”可选字段的长度最多只有 40 字节,而指明一个边界就要用掉4字节(因为序号有32位,需要使用4个字节表示),因此在选项中最多只能指明4个字节块的边界信息。这是因为4个字节块有8个边界,因为需要用 8 * 4 =32个字节来描述。另外还需要两个字节,一个字节用来指明是 SACK 选项,另一个字节是指明这个SACK选项要占用多少字节。如果要报告五个字节块的边界信息,那么至少需要 42 个字节。这就超过了选项字段的40字节的上限。具体是如何规定的,可以参考 RFC 2018。示例表示如下:
在 Linux系统中,有一个内核参数 /proc/sys/net/ipv4/tcp_sack 用来表示SACK选项是否启用或是关闭。默认值是1,即启用SACK选项。 查看该内核参数的命令如下:
如果需要关闭SACK选项,可以使用如下命令修改:
或者在 /etc/sysctl.conf 配置文件中永久修改,添加内容如下:
参考《图解TCP_IP(第5版)》第6章 《计算机网络(第7版-谢希仁)》第5章 《Linux高性能服务器编程》第3章 |
|
网络协议 最新文章 |
使用Easyswoole 搭建简单的Websoket服务 |
常见的数据通信方式有哪些? |
Openssl 1024bit RSA算法---公私钥获取和处 |
HTTPS协议的密钥交换流程 |
《小白WEB安全入门》03. 漏洞篇 |
HttpRunner4.x 安装与使用 |
2021-07-04 |
手写RPC学习笔记 |
K8S高可用版本部署 |
mySQL计算IP地址范围 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/25 18:15:33- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |