Linux套接字:如何检测客户端程序中断开的networking?

我正在debugging基于Linux的套接字程序。 作为网站上提供的所有示例,我应用了以下结构:

sockfd= socket(AF_INET, SOCK_STREAM, 0); connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)); send_bytes = send(sockfd, sock_buff, (size_t)buff_bytes, MSG_DONTWAIT); 

当删除服务器closures其服务器程序时,我可以检测到断开连接。 但是,如果我拔掉以太网电缆,发送function仍然返回正值而不是-1。

如何在客户端程序中检查networking连接,假设我无法更改服务器端?

Solutions Collecting From Web of "Linux套接字:如何检测客户端程序中断开的networking?"

但是,如果我拔掉以太网电缆,发送功能仍然返回正值而不是-1。

首先你应该知道send并不实际发送任何东西,这只是一个存储器复制功能/系统调用。 它将你的进程中的数据复制到内核中 – 稍后内核将获取这些数据,并在将数据打包成分段和数据包之后发送给对方。 因此, send只能返回一个错误,如果:

  • 套接字是无效的(例如伪造文件描述符)
  • 连接显然是无效的,例如它尚未建立或已经以某种方式终止(FIN,RST,超时 – 见下文)
  • 没有更多的空间来复制数据

重点是send不发送任何东西,因此它的返回码不会告诉你有关数据到达另一端的任何信息

回到你的问题,当TCP发送数据时,它需要在合理的时间内进行有效的确认。 如果没有得到,它会重新发送。 多久重新发送一次? 每个TCP堆栈都有不同的做法,但是规范是使用指数退避。 也就是说,先等待1秒,然后是2,然后是4,依此类推。 在某些堆栈上,这个过程可能需要几分钟。

主要的一点是,在中断的情况下, 只有在严重的沉默时间之后, TCP才会声明连接已经中断(在Linux上,它执行了15次重试 – 超过5分钟)。

解决这个问题的一个方法是在你的应用程序中实现一些确认机制。 你可以例如发送一个请求到服务器“ 5秒内回复,否则我会声明这个连接已经死了 ”,然后recv超时。

要检测远程断开连接,请执行read()

检查这个线程的更多信息:

可以在一个连接的套接字读取()函数返回零字节?

检查返回值,看看它是否等于这个值:

EPIPE
此套接字已连接,但现在连接已断开。 在这种情况下,send将首先生成一个SIGPIPE信号; 如果该信号被忽略或阻塞,或者其处理程序返回,那么发送失败和EPIPE。

还要在你的处理程序中添加一个SIGPIPE信号的检查,使其更加可控。

只有调用write()函数才能检测到拔掉的以太网电缆。 这是因为tcp堆栈在没有你意识的情况下进行tcp重传。 这里是解决方案。

即使您已经为您的应用程序套接字设置了keepalive选项,但您的应用程序一直在套接字上写入的情况下,您无法及时检测套接字的死连接状态。 这是因为内核tcp堆栈的tcp重传。 tcp_retries1和tcp_retries2是用于配置tcp重传超时的内核参数。 由于通过RTT机制计算,很难预测重发超时的精确时间。 你可以在rfc793中看到这个计算。 (3.7。数据通信)

https://www.rfc-editor.org/rfc/rfc793.txt

每个平台都有用于tcp重传的内核配置。

 Linux : tcp_retries1, tcp_retries2 : (exist in /proc/sys/net/ipv4) 

http://linux.die.net/man/7/tcp

 HPUX : tcp_ip_notify_interval, tcp_ip_abort_interval 

http://www.hpuxtips.es/?q=node/53

 AIX : rto_low, rto_high, rto_length, rto_limit 

http://www-903.ibm.com/kr/event/download/200804_324_swma/socket.pdf

如果你想早期检测到死连接,你应该为tcp_retries2设置较低的值(默认值为15),但是这并不像我已经提到的那样精确。 另外,目前您不能仅为单个套接字设置这些值。 这些是全局内核参数。 有一些尝试使用单个套接字的tcp重传套接字选项( http://patchwork.ozlabs.org/patch/55236/ ),但我不认为它被应用到内核主线。 我无法在系统头文件中找到这些选项定义。

作为参考,你可以通过像下面这样的“netstat –timers”监控你的Keepalive套接字选项。 https://stackoverflow.com/questions/34914278

 netstat -c --timer | grep "192.0.0.1:43245 192.0.68.1:49742" tcp 0 0 192.0.0.1:43245 192.0.68.1:49742 ESTABLISHED keepalive (1.92/0/0) tcp 0 0 192.0.0.1:43245 192.0.68.1:49742 ESTABLISHED keepalive (0.71/0/0) tcp 0 0 192.0.0.1:43245 192.0.68.1:49742 ESTABLISHED keepalive (9.46/0/1) tcp 0 0 192.0.0.1:43245 192.0.68.1:49742 ESTABLISHED keepalive (8.30/0/1) tcp 0 0 192.0.0.1:43245 192.0.68.1:49742 ESTABLISHED keepalive (7.14/0/1) tcp 0 0 192.0.0.1:43245 192.0.68.1:49742 ESTABLISHED keepalive (5.98/0/1) tcp 0 0 192.0.0.1:43245 192.0.68.1:49742 ESTABLISHED keepalive (4.82/0/1) 

另外,当keepalive timeout ocurrs时,你可以根据你使用的平台来满足不同的返回事件,所以你不能仅仅通过返回事件来决定死亡连接状态。 例如,HP返回POLLERR事件,并且在发生Keepalive超时时AIX返回POLLIN事件。 您当时会在recv()调用中遇到ETIMEDOUT错误。

在最近的内核版本(从2.6.37开始)中,可以使用TCP_USER_TIMEOUT选项将会很好的工作。 这个选项可以用于单个插座。

最后,你可以使用MSG_PEEK标志来读取函数,这可以让你检查socket是否正常。 (MSG_PEEK只是查看数据是否到达内核堆栈缓冲区,而不会将数据复制到用户缓冲区。)因此,您可以使用此标志来检查套接字是否正常,没有任何副作用。