TCP协议的超时详解
众所周知,TCP协议是一个可靠的的协议。TCP的可靠性依赖于大量的 Timer 和 Retransmission 。现在咱们就来细说一下TCP协议的那些Timer在TCP三次握手创建一个连接时,以下两种情况会发生超时:1.client发送SYN后,进入SYN_SENT状态,等待server的SYN+ACK。2.server收到连接创建的SYN,回应SYN+ACK后,进入SYN_RECD状态,等待client的ACK。当超时发生时,就会重传,一直到75s还没有收到任何回应,便会放弃,终止连接的创建。但是在Linux实现中,并不是依靠超时总时间来判断是否终止连接。而是依赖重传次数:当三次握手成功,连接建立,发送TCP segment,等待ACK确认。如果在指定时间内,没有得到ACK,就会重传,一直重传到放弃为止。Linux中也有相关变量来设置这里的重传次数的:当一方接受到TCP segment,需要回应ACK。但是不需要 立即 发送,而是等上一段时间,看看是否有其他数据可以 捎带 一起发送。这段时间便是 Delayed ACK Timer ,一般为200ms。如果某一时刻,一方发现自己的 socket read buffer 满了,无法接受更多的TCP data,此时就是在接下来的发送包中指定通告窗口的大小为0,这样对方就不能接着发送TCP data了。如果socket read buffer有了空间,可以重设通告窗口的大小在接下来的 TCP segment 中告知对方。可是万一这个 TCP segment 不附带任何data,所以即使这个segment丢失也不会知晓(ACKs are not acknowledged, only data is acknowledged)。对方没有接受到,便不知通告窗口的大小发生了变化,也不会发送TCP data。这样双方便会一直僵持下去。TCP协议采用这个机制避免这种问题:对方即使知道当前不能发送TCP data,当有data发送时,过一段时间后,也应该尝试发送一个字节。这段时间便是 Persist Timer 。TCP socket 的 SO_KEEPALIVE option,主要适用于这种场景:连接的双方一般情况下没有数据要发送,仅仅就想尝试确认对方是否依然在线。目前vipbar网吧,判断当前客户端是否依然在线,就用的是这个option。具体实现方法:TCP每隔一段时间(tcp_keepalive_intvl)会发送一个特殊的 Probe Segment,强制对方回应,如果没有在指定的时间内回应,便会重传,一直到重传次数达到 tcp_keepalive_probes 便认为对方已经crash了。当主动关闭方想关闭TCP connection,发送FIN并且得到相应ACK,从FIN_WAIT_1状态进入FIN_WAIT_2状态,此时不能发送任何data了,只等待对方发送FIN。可以万一对方一直不发送FIN呢?这样连接就一直处于FIN_WAIT_2状态,也是很经典的一个DoS。因此需要一个Timer,超过这个时间,就放弃这个TCP connection了。TIME_WAIT Timer存在的原因和必要性,主要是两个方面:主动关闭方发送了一个ACK给对方,假如这个ACK发送失败,并导致对方重发FIN信息,那么这时候就需要TIME_WAIT状态来维护这次连接,因为假如没有TIME_WAIT,当重传的FIN到达时,TCP连接的信息已经不存在,所以就会重新启动消息应答,会导致对方进入错误的状态而不是正常的终止状态。假如主动关闭方这时候处于TIME_WAIT,那么仍有记录这次连接的信息,就可以正确响应对方重发的FIN了。一个数据报在发送途中或者响应过程中有可能成为残余的数据报,因此必须等待足够长的时间避免新的连接会收到先前连接的残余数据报,而造成状态错误。但是我至今疑惑的是:为什么这个超时时间的值为2MSL?如果为了保证双方向的TCP包要么全部响应完毕,要么全部丢弃不对新连接造成干扰,这个时间应该是:被动关闭方LAST_ACK的超时时间 + 1MSL因为被动关闭方进入LAST_ACK状态后,假设一直没有收到最后一个ACK,会一直重传FIN,一直重传次数到达TCP_RETRIES放弃,将这个时间定义为「被动关闭方LAST_ACK的超时时间」,接着必须等待最后一个重传的FIN失效,需要一个MSL的时间。这样才能保证所有重传的FIN包失效,不干扰新连接吧。TCP/IP Illustrated

TCP 可靠传输的实现(二)TCP的重传机制
TCP使用可靠的传输协议,即意味着必须按序、无差错的传送数据到目的端,那么如果在传输过程中发送的包丢失了该怎么办?TCP的重传机制就是:如果发送方认为发生了丢包现象就重发这些数据包。显然,我们需要一个方法去 猜测是否发生了丢包 。最简单的想法就是,接收方每接收到一个包就向发送者返回一个ACK,表示自己已经收到了这段数据,反过来,如果发送方一段时间内没有收到ACK,就知道 很可能是数据包丢失 了,紧接着就重发该数据包,直到收到ACK为止。 为什么是 猜测 呢? 因为即使是超时了,这个数据包也可能并没有丢,它只是绕了段远程,来的很晚而已。毕竟TCP协议是位于传输层的协议,不可能明确知道数据链路层和物理层发生了什么。但是这并不妨碍我们的超时重传机制,因为接收方会自动忽略收到的重复的包。下面我们具体讲一讲TCP的重传机制:这种机制下,每个数据包都有相应的计时器,当超过指定的时间后,没有收到对方的 ACK 确认应答报文就会重发该数据包。超时时间应该设置为多少我们先来了解一下RTT (Round-Trip Time 往返时延)而超时时间是以RTO(Retransmission Timeout 超时重传时间) 表示。超时时间不宜设置的过长或过短,否则:综上可知,RTO设置的值应该略大于RTT的值。RTO值的计算:https://blog.csdn.net/JXH_123/article/details/27345151值得注意的是:每触发一次超时重传,都 会将下一次超时时间间隔设为先前值的两倍 。遇到超时说明网络环境差,不宜频繁发送。Wireshark 抓包显示:超时重传存在的问题是:当一个报文段丢失时,会等待一定的超时时间后才重传,增加了端到端的时延;当一个报文段丢失时,在其等待超时的过程中,可能会出现这种情况: 其后的报文段已经被接收端接收但却迟迟得不到确认,发送端就也以为丢失了,从而引起不必要的重传,既浪费时间也浪费资源。(例如: 数据包5丢失,数据包6、7、8、9都已到达接收方,这个时候客户端只能等服务端发送ACK,因此对于客户端来说,它完全不知道丢了几个包,可能就悲观的认为:5后面的数据包都丢了,就重传这5个数据包,这就比较浪费了)。刚刚提到过,基于计时器的重传往往要等待很长时间,而快速重传使用了很巧妙的方法来解决这个问题。快速重传(Fast Retransmit)机制 不以时间为驱动,而是以数据为驱动重传。由于TCP采用的是累计确认机制,当接收端收到比期望序号大的报文段时,便会重复发送最近一次确认的报文段的确认号,即冗余 ACK (Duplicate ACK)。这样,如果在超时重传定时器溢出之前,接收到连续的三个重复冗余 ACK (第一个ACK是正常的,后三个是冗余的),发送端便知晓哪个报文段在传输过程中丢失了,于是重发该报文段,而不需要等待超时重传定时器溢出,大大提高了效率。Wireshark 抓包显示:但是,快速重传仍然没有解决第二个问题:到底该重传多少个包?改进的方法就是 SACK (Selective Acknowledgment),简单来说就是在快速重传的基础上,返回最近收到的报文段的序列号范围,这样客户端就知道,哪些数据包已经到达服务器了。看下例子:存在 SACK 选项时当500-599报文到达,接收方发送 ACK 200 ,SACK [500,600)当600-699报文到达,接收方发送 ACK 200 ,SACK [500,700)当700-799报文到达当800-899报文到达当900-999报文到达,接收方累积确认发送 ACK 200 ,SACK [500,1000)连续收到3个重复ACK,发送方经检查发现200-499的数据丢失了,执行快速重传,待接收方接收到200-499的数据,并返回 ACK 1000时,发送方的所有数据均已确认完毕,移动滑动窗口到1000位置处。使用 SACK可以告知发送方 收到了哪些数据,发送方收到这些消息后就会知道哪些数据丢失,然后立即重传丢失的部分。需要注意的是: 只有收到失序的分组时才可能会发送SACK 。SACK 包括了两个TCP选项,一个选项用于标识是否支持 SACK(SACK_Permitted),在TCP建立连接时发送;另一种选项则包含了具体的 SACK信息。(1)SACK_Permitted 选项该选项只允许在TCP连接建立时,有 SYN标志的包中设置,在连接建立阶段,主动发起连接的一方在它的SYN中指定选项。只有在它从另一方的SYN中收到了这个选项之后,SACK机制才会被使能。(2)SACK 信息选项SACK 选项参数告诉对方 已经接收到 并缓存的不连续的数据块,发送方可据此信息检查究竟是哪个块丢失,从而发送相应的数据块。Left Edge:本区块的第一个序号。 Right Edge:本区块的最后序号的下一个序号。[Left Edge, Right Edge)区间的ACK 序号表示本次确认收到的序号。问题1:SACK选项最多能包含多少个需重传的块?由于TCP首部的最大长度为 60 byte,而固定首部占用了 20 byte,对于SACK选项本身占用了2 byte,所以剩下 60-20-2=38 byte。而每个块(包括开始和结束)占用 8 byte,所以最多可标识的块数为 38/8 = 4块,所以 SACK 最多可以包括4个需重传的块。同时由于SACK有些时候会和时间戳(占10字节)一起用,因此,此种情况下最多只有3个SACK。问题2:SACK选项的使用规则是怎么样的?SACK 的发送方,即 报文的接收端第一个块需要指出是哪一个到达的报文触发的 SACK尽可能多的把所有的块填满SACK 要报告最近接收的不连续的数据块SACK 的接收端,即 报文的发送端:数据没有被确认前,都会保持在滑动窗口内每个数据包都有一个 SACKed 的标志,对于已经标示的报文,再次接收到时会忽略如果SACK丢失,超时重传之后,重置所有数据包SACKed 标志DSACK是在SACK的基础上做了一些 扩展 ,主要用于对收到的 重复报文 进行了处理。它的主要作用是:告诉发送方有哪些数据被重复接收了。DSACK同样使用了与SACK一样的报文格式,唯一区别在于: 第一个连续的block指定的是触发DSACK的重复报文的序号空间。如果第一个段的范围被ACK范围所覆盖,那么就是DSACK。或者,第一个段的范围被SACK的第二个段覆盖,那么就是DSACK。引入DSACK的好处有:1)可以让发送方知道,是发出去的包丢了,还是回来的ACK包丢了;2)是不是自己的 timeout 设置太小了,导致重传;3)网络上出现了先发的包后到的情况(又称数据包失序);4)网络上是不是把我的数据包给复制了;总之,DSACK的目的是帮助发送方判断,是否发生了包失序、ACK丢失、包重复或伪重传,让TCP可以更好的做网络流量控制。 超时重传机制能解决数据包丢失的问题,但是超时重传机制存在等待时间太长,浪费时间在等待上,降低了传输效率和无法知道需要重传哪些数据包的问题。 快速重传能解决超时重传的等待时间太长的问题,但是对于究竟该重传哪些包的问题仍然不能有效解决。SACK能需要重传哪些数据包的问题,它可以知道哪些包是被确认接收的,客户端能据此判断需要重传的包。DSACK则是作为SACK的一个辅助措施,可以用来判断网络究竟是出现了什么情况,据此做好网络流量控制。

如何修改 tcp/ip 最大重传超时时间
Windows 提供了一种机制来控制初始重新传输的时间,并重新传输时间然后动态地自行调整。若要更改的初始重新传输时间,修改以下注册表值。 更改 Windows 2003 和 Windows XP 中,Windows 2000 中的以下子项:HKEY_LOCAL_MACHINESystemCurrentControlSetServicesTcpipParametersInterfaces适配器 IDValue Name:TCPInitialRttData Type: REG_DWORDValid Range: 300-65535 (milliseconds in decimal)Default: 0xBB8 (3000 milliseconds expressed in hexadecimal)说明: 此参数控制 TCP 使用对每个新连接的初始重新传输超时。它适用于连接请求 (SYN) 和每个连接发送的第一个数据段。例如的"十进制 5000"值数据将初始的重新传输时间设置为 5 秒。 注意: 您可以增加将初始超时的值。降低此值不受支持。

tcp如何设置超时重传时间
TCP通过在发送时设置一个定时器来解决这种问题。如果当定时器溢出时还没有收到确认,它就重传该数据。对任何实现而言,关键之处就在...

TCP(II) 超时重传
查看原文图中黑色那条就是因为定时器超时仍没有收到 ACK,所以引起了发送方超时重传。实际上 TCP 有两个阈值来决定如何重传同一个报文段:一是愿意重传的次数 R1、二是应该放弃当前连接的时机 R2。R1 和 R2 的值应分别至少为 3 次和 100 秒,如果超过任何一个但还没能重传成功,会放弃该连接。当然这两个值是可以设置的,在不同系统里默认值也不同。那么如何设定一个合适的超时的值呢?假设 TCP 工作在静态环境中,这很容易,但真实网络环境不断变化,需要根据当前状态来设定合适的值。RTO(retransmission timeout)一般是根据RTT(round trip time)也就是往返时间来设置的。若 RTO 小于 RTT,则会造成很多不必要的重传;若 RTO 远大于 RTT,则会降低整体网络利用率,RTO 是保证 TCP 性能的关键。并且不同连接的RTT不相同,同一个连接不同时间的 RTT 也不相同,所以 RTO 的设置一直都是研究热点。所以凭我们的直觉,RTO 应该比 RTT 稍大:RTO=RTT+Δt那么,RTT 怎么算呢:SRTT=(1−α)×SRTT+α×RTTnewSRTT(smooth RTT),RTTnew 是新测量的值。如上,为了防止 RTT 抖动太大,给了一个权值a,也叫平滑因子。a 的值建议在 10%~20%。举个例子,当前 RTTs=200ms,RTTs=200ms,最新一次测量的 RTT=800ms,RTT=800ms,那么更新后的 RTTs=200×0.875+800×0.125=275ms,RTTs=200×0.875+800×0.125=275ms.Δt如何得到呢?RFC 2988 规定:RTO=SRTT+4×RTTD因此,按照上面的定义,Δt=4×RTTD. 而 RTTD 计算公式如下:RTTD=(1−β)×RTTD+β×|SRTT−RTTnew|实际上,RTTD 就是一个均值偏差,它就相当于某个样本到其总体平均值的距离。这就好比你的成绩与你班级平均成绩差了多少。RFC 推荐 β=0.25。根据前面的公式,我们可以得到 RTO。一旦超过 RTO 还没收到 ACK,就会引起发送方重传。但如果重传后还是没有在 RTO 时间内收到 ACK,这时候会认为是网络拥堵,会引发 TCP 拥塞控制行为,使 RTO 翻倍。则第 n 次重传的 RTOn 值为:RTOn=2^(n−1)×RTO1下图是一个例子:如上,在时间为0.22531时开始第一次重传,此后重传时间开始指数增大,(在尝试了8次后停下,说明其 R2 的值可能为8)。前面说了 RTO 的公式,它和 RTT 有关,那么每一次的 RTT 是如何得到的呢?在之前 TCP 连接管理的时候讲过,TCP有一个 TSOPT (timestamp option) 选项,它包含两个时间戳值。它允许发送者在报文中带上一个32比特的时间戳值(TSV),然后接收方 将收到的值原封不动的填入 ACK 报文段中 TSOPT 选项的第二部分,时间戳回显字段(TSER)。发送方收到 ACK 以后,将当前时间戳减去 TSOPT 选项的 TSER 就可得到精确的RTT值。还有另一个重要的细节,如果测量 RTT 的样本出现了超时重传,但是我们收到了 ACK 时无法分辨是对哪一次的确认,这时候 RTT 的值可能是不正确的。因此,Karn 算法规定:此时不更新 RTTnew 的值。并且如果发生再次重传,则采用退避后的 RTO 的值,直到发送成功,退避指数重新设定为 1 。假设有三个数据包依次发送,1号和3号先到达,2号数据包由于网络因素最后到达。接收方收到3号时,会发送一个1号的冗余 ACK,然后2号到达,此时会发送一个3号的累积 ACK 表明这三个到达。在这个例子里,3号 ACK 并没有立即返回,发送方收到3号的 ACK 后,根据其 TSER 计算此时的 RTT,就会导致发送方过高的估计 RTT,降低重传积极性,使得 RTO 相应增大,当然这在失序时是有好处的,因为过分积极会导致大量的伪重传。如下图,在发送第四个 ACK 后出现延迟高峰,导致发送方在 RTO 时间内没有收到 5 ~ 8 的 ACK,于是发生重传,然后之前的 ACK 到达,于是又依次发送 6 ~ 8,就导致了不必要的重传。可以用 Eifel 算法来解决(略)。

从前面可以看出,TCP 可以学习链路特征,如 RTT、SRTT 等,但一旦连接关闭,这些信息就会丢失。即使相同的接收方与发送方建立新的连接,也必须从头开始“学习”。较新的 TCP 实现维护了这些值,在 Linux 中可以通过如下命令查看:在大多数情况下,计时器超时并触发重传是不必要的,也不是期望的,因为 RTO 通常是大于 RTT(约2倍或更大),因此基于计时器的重传会导致网络利用率降低。首先我们要知道,接收方收到失序报文段时,应立即生成确认信息(重复 ACK),以便发送方尽快、高效地填补空缺。而发送方在收到重复 ACK 时,无法判断是由于数据包丢失还是仅仅因为延迟,所以发送方等待一定数目的重复 ACK (重复 ACK 阈值,dupthresh),这时可以认为是数据包丢失,即便还未超时,也立即发送丢失的分组。所以快速重传概括如下:TCP 发送方在观测到至少 dupthresh ( 通常是 3 ) 个重复 ACK,立即重传,而不必得到计时器超时。当然也可以同时发送新的数据。当然快速重传也会造成一些问题。在轻微失序的情况下(左图),不会有什么影响。但在严重失序时(右图),4号数据包延迟到达,接收方发送 4 个冗余 ACK ,让发送方认为 4 号分组丢失,造成伪快速重传。尽管可能性较小,但 IP 协议也可能将单个包传输多次。假如 IP 协议将一个包传输了 4 次,然后发送方接收到 3 个冗余 ACK ,也会让发送方以为分组丢失,导致伪快速重传。在上一篇文章中提到过 TCP 的 SACK 选项,它通过若干个 SACK 块来帮助发送方知道接收方有哪些空缺,可以减少不必要的重传。接收端在 TCP 连接建立期间收到 SACK 选项即可生成 SACK。通常来说,当收到失序报文段,接收方就会生成 SACK。第一个 SACK 块包含的应该是最近收到的报文段的序列号范围。由于 SACK 选项空间有限,应尽可能向发送方提供最新信息。其余的 SACK 按先后顺序依次排列,也就是说,该 ACK 报文段除了包含最新接收的序列号信息,还应重复之前的 SACK 信息。这是因为 ACK 报文段是没有重发机制的,可能会丢失,重复提高了其鲁棒性。发送方应该充分利用 SACK 信息来进行重传,称为选择性重传。发送方记录累积 ACK 信息和 SACK 信息,当接收到相应序列号范围内的 ACK 时,在其重传缓存中标记该报文段重传成功。

本文由 在线网速测试 整理编辑,转载请注明出处,原文链接:https://www.wangsu123.cn/news/45559.html。