TCP系列12—重传—2、Linux超时重传引入示例

        在前面我们概述了TCP的超时重传之后我们简单的看一下tcp超时重传的示例。首先简单的描述一下测试过程

1、设置/proc/sys/net/ipv4/tcp_early_retrans为2,关掉TLP功能(后面内容介绍TLP)。设置/proc/sys/net/ipv4/tcp_retries2为8,减少重传次数,这样方便wireshark抓包演示。同时设置/proc/sys/net/ipv4/tcp_discard_on_port为9877,以让client可以精确的控制发出的TCP报文,而不受到内核TCP模块的影响。

2、client通过raw socket直接在IP层之上构造TCP报文,与server通过三次握手建立连接,其中client为127.0.0.1:10000,server为127.0.0.1:9877。这个步骤对应No1--No3报文。

3、TCP连接建立后client发送"hello"消息给服务器,服务器则回复ACK,这个步骤对应No4--No5报文。

4、server发送“hello”给client,这个步骤对应No6报文。

5、client收到server的"hello"报文后,直接丢弃并不回复ACK。

6、server在没有收到ACK报文的情况下,过了大约1.5s后,server端RTO超时,触发超时重传。client同样在收到这些重传报文的时候直接丢弃而不回复ACK,这样server持续重传。一共重传了6次。

这个过程的wireshark截图如下(为了方便观察RTO,我把No6初传设置为时间参考点,后面数据包的Time时间都是相对于No6包的时间):

从这个超时重传示例中我们重点关注几个方面

1、可以看到No7第一次重传与No6初传间隔大约为1.5s,No8第二次重传与No7时间间隔大约是3.0s,No9第三次重传与No8时间间隔大约是6.0s。可以看到每次重传后,重传的时间间隔(即RTO)都是上次重传时间间隔的2倍。实际我们从server端程序可以获取到Linux内核中TCP模块计算的RTO分别为1.504s、3.008s、6.016s。也可以精确的看到RTO的倍增关系。这种RTO倍增关系就是指数回退(binary exponential backoff)

2、我们在测试过程描述中说明设置tcp_retries2为8,而从wireshark中可以看到实际只是发生了6次重传。实际上linux内核会根据tcp_retries2计算出一个timeout,计算方式如下面代码所示,其中对于established状态下的endpoint,rto_base为200ms,TCP_RTO_MAX为120000ms。当tcp_retries2设置为8的时候,按照下面流程计算得出timeout=102.2s。其中No11和No12之间的RTO为48s左右(根据server端实际获取的信息,linux内部实际计算出的RTO为48.128s),如果进一步指数回退,则下一次重传时间点大约是48*2+94=190s,可以看到190s已经超过了计算出的timeout(即102.2s)值,应该不会进一步进行重传,多次超时重传失败server端则会直接释放这个tcp连接,释放这个连接后如果server端尝试进一步写入数据就会返回ETIMEDOUT的错误

  1. if (tcp_retries2 <= 9)
  2. timeout = ((2 << tcp_retries2) - 1) * rto_base;
  3. else
  4. timeout = ((2 << 9) - 1) * rto_base +
  5. (tcp_retries2 - 9) * TCP_RTO_MAX;

在RFC1122中有两个门限R1和R2,当重传次数超过R1的时候,TCP向IP层发送negative advice,指示IP层进行MTU探测、刷新路由等过程,以防止由于网络链路发生变化而导致TCP传输失败。当重传次数超过R2的时候,TCP放弃重传并关闭TCP连接。其中R1和R2也可以表述为时间,即总重传时间超过R1或者R2的时候触发响应的操作。在linux中对于普通数据报文状态下的TCP,R1对应/proc/sys/net/ipv4/tcp_retries1,R2对应/proc/sys/net/ipv4/tcp_retries2参数。这两个参数都是根据上面的计算流程计算出一个timeout值,当总重传时间超过这个timeout值还没有收到ack的时候触发响应的操作。对于SYN报文如我们之前所讲,则是由tcp_syn_retries和tcp_synack_retries这两个参数控制


补充说明:

1、linux中根据tcp_retries1和tcp_retries2计算timeout的过程参考代码retransmits_timed_out。

2、网上很多资料以及man 7 tcp和第二版的tcpip详解中对于tcp_retries1或tcp_retries2描述都是按照重传次数来描述的,实际上是错误的。








posted @ 2016-11-07 13:47  lshs  阅读(10107)  评论(4编辑  收藏  举报