一、前言

在TCP通信中,既要保证在网络正常的情况下提供可靠的交付服务,又要保证在网络异常的情况下也提供可靠的交付服务。而TCP的超时重传机制就是解决在网络异常情况下的可靠传输问题的。

二、通过序列号和确认应答提供可靠性

在TCP通信中,当发送端的数据到达接收端时,接收端会返回一个已收到消息的通知。这个通知消息叫做确认应答(ACK)。

ACK(Positive Acknowledgement) 意指已经接收。

2.1 正常数据传输过程

正常数据传输

可以看到,TCP通过确认应答机制实现可靠的数据传输服务。

2.2 异常数据传输过程

当发送端将数据发出后,会等待对端的确认应答。如果有确认应答,说明数据已经成功到达对端。否则,数据可能已经丢失。

在一定时间内没有等到确认应答,发送端会认为数据已经丢失,并进行重发。这样,即使有丢包,仍能保证数据达到对端,实现可靠传输。

数据丢失的情况

发送端未收到确认应答,不一定是数据丢失。也可能是对端已经收到数据,只是返回的确认应答在途中丢失。这种情况也会导致发送端因没有收到确认应答,而认为数据没有到达目的地,从而进行重发。

确认应答丢失的情况

此外,也有可能是因为一些其他原因导致确认应答延迟到达,在发送端重发数据后才收到。此时,源发送方只要按照重发机制重发数据即可,但是对于接收方来说,它会反复收到相同的数据。而为了对上层应用提供可靠的传输,必须得放弃重复的数据报文段。

每一次传输数据时,TCP报文段的首部都会标明本报文段的起始序列号,以便对方确认。当接收方收到数据后,会去查询数据报文段首部中的序列号和数据的长度,将自己下一步应该接收的起始序号作为确认应答回复给发送方。就这样,通过序列号和确认应答号,TCP就可以实现可靠传输。

 序列号

序列号是按顺序给TCP报文段的数据部分的每一个字节都标上号码的编号。

序列号的初始值并非是从0开始的,而是在建立连接时又随机数生成的。而后面的序号值计算则是对每一字节加1。

确认号

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值使用如下的公式计算得到:

新的RTTs = (1 - α) × (旧的RTTs) + α × (新的RTT样本)

<说明1> 新的RTT样本需要重新测量得到。

<说明2> 系数 α 的取值范围为[0, 1)。

<说明3> RFC 6298文档推荐的 α 值为 1/8,即 0.125。

(2)基于计算得到的RTTs时间设置超时重传时间RTO。显然,超时计时器设置的超时重传时间RTO应略大于上面得到的加权平均往返时间RTTs。

RFC 6298 建议使用下式计算RTO:

RTO = RTTs + 4 × RTTd

<说明> RTTd 是 RTT的偏差的加权平均值,它与 RTTs 和 新的 RTT 样本之差有关。

RFC 6298 建议这样计算RTTd。当第一次测量时,RTTd的值取为测量到的RTT样本值的一半。在以后的测量中,则使用下式计算加权平均的RTTd:

新的RTTd = (1 - β) × (旧的RTTd) + β × |RTTs - 新的RTT样本值|

<说明1> 系数 β 的取值范围为[0, 1),它的推荐值为 1/4,即 0.25。

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。

查看这两个内核参数的默认值:

# sysctl -a |grep -e tcp_retries
net.ipv4.tcp_retries1 = 3
net.ipv4.tcp_retries2 = 15

前者指定在底层IP接管之前TCP最少执行的重传次数。默认值是3。

后者指定连接放弃前TCP最多可以执行的重传次数。默认值是15。(一般对应13~30min)。

四、选择确认SACK

问题:如果接收方收到的报文段无差错,只是未按序到达,中间还缺了一些序号的字节数据,那么能否设法只重传缺少的数据而不重传已经正确到达接收方的数据呢?

SACK(Selective ACKonwledgement,选择确认)

答案是可以的,选择确认就是一种可行的处理方法。下面我们用一个例子来说明选择确认的工作原理。

当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。示例表示如下:

kind = 4:占1字节,表示选项类别为 SACK。length:占1字节,表示SACK选项总共占用的字节数。该长度包括了kind字段和length字段占据的2个字节。

kind = 5:占1字节,表示这是SACK实际工作的选项。legnth:N 表示字节块数量;每个字节块有两个边界信息,因此需要8字节;+2 表示加上kind和length这两个字节。

在 Linux系统中,有一个内核参数 /proc/sys/net/ipv4/tcp_sack 用来表示SACK选项是否启用或是关闭。默认值是1,即启用SACK选项。

查看该内核参数的命令如下:

# sysctl -a | grep tcp_sack
net.ipv4.tcp_sack = 1

如果需要关闭SACK选项,可以使用如下命令修改:

sysctl -w net.ipv4.tcp_sack = 0

或者在 /etc/sysctl.conf 配置文件中永久修改,添加内容如下:

net.ipv4.tcp_sack = 0

参考

《图解TCP_IP(第5版)》第6章 - TCP与UDP

《计算机网络(第7版-谢希仁)》第5章 - 运输层

《Linux高性能服务器编程》第3章 - TCP协议详解

TCP协议-TCP超时重传机制相关推荐

  1. TCP之超时重传机制

    TCP协议是一种面向连接的可靠的传输层协议,它保证了数据的可靠传输,对于一些出错.超时丢包等问题TCP设计了超时重传机制,其基本原理:在发送一个数据之后,就开启一个定时器,若是在这个时间内没有收到发送 ...

  2. TCP协议可靠性保证(确认应答机制,超时重传机制,流量控制,拥塞窗口)

    上一次我们知道了TCP协议通过连接管理机制保证可靠性,今天我们继续来看一看TCP协议中其他几种保证可靠性的方法. · 确认应答机制  · 超时重传机制  · 流量控制  · 拥塞窗口 确认应答机制  ...

  3. 4-5:TCP协议之确认应答(ACK)机制和超时重传机制

    文章目录 一:TCP的确认应答(ACK)机制 二:超时重传机制 一:TCP的确认应答(ACK)机制 在TCP中,当发送端的数据达到接收主机时,接收端主机会返回一个已收到消息的通知,这个消息叫做ACK( ...

  4. TCP/IP协议栈:TCP超时重传机制

    目录 基础概念 重传超时时间RTO RTO的设定 连接往返时间RTT RTT的计算 Karn算法 往返时间测量 重传 拥塞避免算法 快速重传和快速恢复算法 重新分组 网络数据包丢失,重传和重复确认 是 ...

  5. 计算机网络超时重传时间,TCP超时重传机制

    超时重传是TCP协议保证数据可靠性的另一个重要机制,其原理是在发送某一个数据以后就开启一个计时器,在一定时间内如果没有得到发送的数据报的ACK报文,那么就重新发送数据,直到发送成功为止.[1] 中文名 ...

  6. TCP第三次握手失败的处理(Server端超时重传机制、RST包响应、SYN攻击)

    面试题: 在 TCP 建立连接的三次握手连接阶段,如果客户端发送的第三个ACK包丢了,那么客户端和服务端分别进行什么处理呢? 相信了解 tcp 协议的人,三次握手的过程肯定很了解了.第三次的 ack ...

  7. 网络原理 | 传输层重点协议之TCP协议(TCP连接的三次握手与四次挥手、TCP的安全机制与效率机制)

    目录 TCP协议 安全机制 确认应答机制 超时重传机制 连接管理机制 三次握手 四次挥手 流量控制机制 ​编辑拥塞控制机制 效率机制 滑动窗口机制 延迟应答机制 捎带应答机制 TCP协议 · 传输层的 ...

  8. 公开课:TCP协议的可靠性传输机制-王达-专题视频课程

    公开课:TCP协议的可靠性传输机制-11821人已学习 课程介绍         本课程是2015年10月14号晚上在CSDN学院进行的一场公开课,主讲TCP协议的几个方面的传输机制,主要包括以下几个 ...

  9. TCP协议-TCP服务特点和头部结构

    TCP服务特点 面向连接.基于字节流和可靠传输. TCP的面向连接是什么意思? 通信双方都必须先建立连接,然后才能开始数据的读写,双方都必须为该连接分配必要的内核资源,以管理连接的状态和连接上数据的传 ...

最新文章

  1. CSS3颜色不透明度如何设置
  2. Puppet学习之文件管理
  3. ompl_interface/OMPLPlanner
  4. python 共享内存变量_浅谈python多进程共享变量Value的使用tips
  5. jquery easyui combobox设置默认选中第一项
  6. matplotlib 第二次执行报错在 django web服务中
  7. linux shell之删除当前文件夹不包含文件1和文件2的其他所有文件
  8. 数论 —— 线性同余方程组与中国剩余定理
  9. 高品质UI设计模板|PSD下载,设计师的最佳临摹素材
  10. 面试被问如何保证缓存与数据库的双写一致性?这篇帮你搞定
  11. 国标GB28181协议紫光华智云平台级联国标GB28181协议视频平台EasyGBS注意事项
  12. VS2003远程调试
  13. NAT穿透技术简介和实现方案分析
  14. 无人驾驶技术 CNN应用
  15. 二叉树的镜像和对称二叉树
  16. 双非本科小渣渣的字节跳动Android岗面试题分享(已拿offer,分享攒人品~)
  17. UE4 的 VR 视野破碎或右眼错误的解决方案 —— 从 Ocean Floor Environment 项目
  18. linux服务器上的项目读取本地文件,java访问linux服务器读取文件路径
  19. fdisk in minix 源代码分析
  20. could only be written to 0 of the 1 minReplication nodes.

热门文章

  1. 无法安装office此计算机安装了32位,无法将 64 位 Office 与 32 位 Office 一起安装 | Microsoft Docs...
  2. 无限纷争如何找到以前的服务器,无限纷争远古遗迹及幻境冒险玩法详细讲解
  3. (AAAI-2019)STA:用于大规模基于视频的行人重识别的时空注意力
  4. html5视频作为页面背景
  5. Tita:OKR教练专家的6个OKR实施技巧
  6. git 查看自己秘钥_git生成和检查秘钥操作
  7. 数据结构 之 Prim算法的应用 之 MST(最小生成树)
  8. MUMU模拟器设置网络
  9. 【每天1分钟】MarkDown语法学习之分割线
  10. 《途客圈创业记:不疯魔,不成活》一一2.9 UX再造