网络编程-断网重传

Posted by 周思进 on December 11, 2019

如果客户端和服务端建立TCP连接,客户端正往服务端发送数据的过程中,服务端网络断开了,会是什么现象呢?

可以编写两个简单的测试程序,客户端间隔1S一直往服务端发送一串字符串,同时打印总共已发送的字节数;而服务端也只是简单的不停接收数据,并打印一共接收了多少字节数据。

在不断网的情况下,客户端和服务端两边打印基本同步进行,发送和接收的总字节数一致。

这时将服务端的网线拔掉,可以看到客户端仍旧在正常的输出发送打印,并不停的累加已发送字节数,但服务端因为断网显然是收不到数据的,也就没有继续打印输出了。

通过客户端仍旧继续输出现象,可以发现,对于中间链路断网,客户端是感知不到的。在客户端输入netstat -an命令查看,可以看到客户端和服务端建立的TCP连接仍旧处于ESTABLISHED状态,服务端查看也一样。两边仍旧维持着连接状态。

如果测试程序就如前面说的1S发送一小段字符串,那你会看到客户端一直打印输出在继续进行发送,那既然网络都已经断开了,write接口为什么还可以正常发送返回呢?

因为应用层并不是直接将数据发送给对方,而是先将数据发送到内核发送队列缓冲区。通过netstat -an命令除了看到它们的连接状态维持不变,还会发现Send-Q那列数据在不断增长,而增长的大小就是应用层实际要发送的字节大小。

因为内核队列缓冲区没有满,所以应用层还可以不断调用write接口进行写操作。不过内核队列缓存也是有上限的,当内核发送队列缓存写满时,write接口就会阻塞等待可写,这可以通过减少等待写时间来进行模拟。

虽然说客户端感知不到服务端断网了,但因为客户端在进行数据发送操作,因为数据发送不出去,就会进行TCP重传,而TCP重传的特性最终会让该连接在重传次数耗尽无果的情况下关闭连接。

具体TCP重传次数有如下两个内核配置限制
/proc/sys/net/ipv4/tcp_retries1
/proc/sys/net/ipv4/tcp_retries2

对于这两个字段内核文档说明,摘取如下部分

The default value of 15 yields a hypothetical timeout of 924.6 seconds and is a lower bound for the effective timeout. TCP will effectively time out at the first RTO which exceeds the hypothetical timeout.

对于TCP重传次数我其实不怎么了解,只是本地测了下,客户端在大概15分之后最终断开了连接,和文档中的924.6s是比较接近的

那如果在断开连接之前恢复了网络,服务端还可以正常接收到客户端发送数据么?因为连接状态维持着,网络恢复后,服务端仍旧是可以正常接收到客户端发送的所有数据,包含客户端一直写缓存在内核发送队列中的数据。如果通过抓包查看,则恢复网络后客户端发送的是一个较大的数据包。

不过需要注意的是,并不是网线一接入,服务端就可以正常接收到数据了,这也需要等客户端发起了TCP重传,得到了服务端的响应之后才开发恢复正常的网络数据收发。


1、服务端接收数据长度不能使用strlen(buf)来进行判断,而应该通过read接口返回的读取字节数进行计算

2、如果发送端发送的很快,可以看到收发程序所打印的接收内容不一定是自己完整些的那个字符串形式,因为TCP是字节流。所以对于TCP接收数据的处理操作需要接收方自己明确,可以看聊天服务器-客户端telnet登入