我调试基于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);
当删除服务器关闭其服务器程序,我可以检测到断线。 但是,如果我拔掉网络线,发送功能仍然会返回正值,而不是-1。
如何检查在客户端程序假设我不能改变服务器端的网络连接?
但是,如果我拔掉网络线,发送功能仍然会返回正值,而不是-1。
首先你应该知道send
实际上不发送任何东西,它只是一个内存复印功能/系统调用。 它从过程到内核拷贝数据 - 晚些时候内核将获取数据并在段和包包装之后发送给对方。 因此send
只能返回如果有错误:
- 套接字是无效的(例如假的文件描述符)
- 连接显然是无效的,例如,它尚未建立或者已经以某种方式被终止(FIN,RST,超时 - 见下文)
- 有没有更多的空间来复制数据
主要的一点是, send
不发送任何东西,因此它的返回码不告诉你实际数据到达对方的任何事情 。
回到你的问题,当TCP发送数据,预计在合理时间内的有效确认。 如果没有得到一个,它重新发送。 多久它重新发送? 每个TCP协议栈做不同的事情,但规范是使用指数退避。 也就是说,第一等待1秒,然后如图2所示,然后4等。 在一些堆叠这个过程可能需要几分钟。
主要的一点是,在中断的情况下,TCP将只有认真大型沉寂一段时间后死亡宣告的连接(在Linux上它确实像15重-超过5分钟)。
解决方法之一是实现在应用程序中的一些确认机制。 例如,您可以“ 在5秒钟内答复,否则我就声明这一点上死了 ”向服务器发送一个请求,然后recv
一个超时。
你不仅可以与调用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)较低的值,如果你想早点检测死连接,但它不是,因为我已经说了精确的时间。 此外,目前不仅可以用于单插座设置这些值。 这些都是全球性的内核参数。 有一些试用申请单插槽(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超时ocurrs,可满足您不同的返回事件取决于您使用的平台,所以你不能只有返回事件决定死连接状态。 例如,HP返回POLLERR事件和keepalive超时发生AIX只返回POLLIN事件。 你会满足的recv ETIMEDOUT错误(),在这个时间打电话。
在最近的内核版本(2.6.37以来),你可以使用TCP_USER_TIMEOUT选项将工作做好。 此选项可用于单插座。
最后,您可以使用阅读与MSG_PEEK标志,它可以让你检查插座是好的功能。 (MSG_PEEK只是偷窥数据是否到达内核堆栈缓冲区永不拷贝数据到用户缓冲区。)所以你可以使用这个标志只是为了检查插座是好的,没有任何副作用。
检查返回值,看它是否等于该值:
EPIPE
此套接字连接,但现在连接被中断。 在这种情况下,发送第一生成SIGPIPE信号; 如果信号被忽略或阻塞,或者如果处理程序返回,然后发送失败,EPIPE。
还添加了支票处理函数中的SIGPIPE信号,使其更加可控。