2013-02-08 130 views
6

我正在调试基于c的linux套接字程序。由于在网站上提供的所有例子, 我采用以下结构: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); 

当删除服务器关闭其服务器程序,我可以检测到断线。但是,如果我拔掉以太网电缆,send函数仍返回正值而不是-1。

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

回答

28

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

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

  • 插座是无效的(例如假文件描述符)
  • 的连接显然是无效的,例如,它尚未建立或者已经在一些被终止方式(FIN,RST,超时 - 见下文)
  • 没有更多的空间来复制数据

主要的一点是,send不发送任何东西,因此它的返回码不告诉你什么关于实际到达另一侧的数据

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

重点是,在中断的情况下,TCP将声明一个连接死亡只有在严重的沉默期(在Linux上它做了15次重试 - 超过5分钟)。要解决这个

的方法之一是实现在应用程序中的一些确认机制。例如,您可以“在5秒内回复否则我就声明这一点上死”向服务器发送一个请求,然后用recv超时。

1

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

EPIPE
该插座已连接,但现在连接断开。在这种情况下,send将首先生成一个SIGPIPE信号;如果该信号被忽略或阻止,或者其处理程序返回,则发送失败并显示EPIPE。

还要在您的处理程序中添加对SIGPIPE信号的检查,以使其更易于控制。

0

你不仅可以与调用write()funcation检测拔出以太网电缆。 这是因为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)如果你想及早发现死连接设置较低的值,但它的我已经没有精确的时间了说过。 另外,目前您不能仅为单个套接字设置这些值。这些是全局内核参数。 有一些试用申请单插槽(http://patchwork.ozlabs.org/patch/55236/)TCP重传套接字选项,但我不认为这是应用到内核主线。我无法在系统头文件中找到这些选项定义。

仅供参考,您可以监视通过像下面用“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超时ocurrs,可满足您不同的返回事件取决于您使用的平台,所以你必须不返回事件决定只死连接状态。 例如,当Keepalive超时发生时,HP返回POLLERR事件,并且AIX仅返回POLLIN事件。 您当时会在recv()调用中遇到ETIMEDOUT错误。

在最近的内核版本(自2.6.37开始)之后,你可以使用TCP_USER_TIMEOUT选项将很好的工作。该选项可用于单个套接字。

最后,您可以使用带有MSG_PEEK标志的读取函数,它可以让您检查套接字是否正常。 (MSG_PEEK只是查看数据是否到达内核堆栈缓冲区,并且从不将数据复制到用户缓冲区中。) 因此,您可以使用此标志仅用于检查套接字是否正常,没有任何副作用。

相关问题