如何检测TCP套接字断开(使用C Berkeley套接字)

时间:2011-06-19 17:34:45

标签: c sockets tcp network-programming

我正在使用循环从c Berkeley套接字读出消息,但我无法检测套接字何时断开连接,因此我接受新的连接。请帮忙

while(true) {
            bzero(buffer,256);
            n = read(newsockfd,buffer,255);
            printf("%s\n",buffer);        
}

3 个答案:

答案 0 :(得分:16)

检测套接字连接的唯一方法是写入。

read()/recv()上获取错误将表明连接已断开,但在读取时未收到错误并不表示连接已启动。

您可能有兴趣阅读此内容: http://lkml.indiana.edu/hypermail/linux/kernel/0106.1/1154.html

此外,使用TCP Keep Alive可以帮助区分不活动和断开的连接(通过定期发送内容,即使应用程序没有数据要发送)。

(编辑:删除了@Damon指出的错误句子,谢谢。)

答案 1 :(得分:0)

您的问题是您完全忽略read()返回的结果。 read()之后的代码应该至少看起来像这样:

if (n == 0) // peer disconnected
    break;
else if (n == -1) // error
{
    perror("read");
    break;
}
else // received 'n' bytes
{
    printf("%.*s", n, buffer);
}

接受新连接应该在一个单独的线程中完成,而不依赖于此连接上的流末尾。

bzero()电话无意义,只是先前错误的解决方法。

答案 2 :(得分:-1)

那是因为你没有使用keepalive超时。 在接收端,keepalive socket选项是检测死连接的最佳解决方案。

但是,如果您的应用程序继续写入套接字,则需要考虑更多内容。 即使您已经为应用程序套接字设置了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超时时,您可以根据您使用的平台遇到不同的返回事件,因此您不能仅通过返回事件来确定死连接状态。 例如,当发生keepalive超时时,HP返回POLLERR事件,AIX仅返回POLLIN事件。 那时你将在recv()调用中遇到ETIMEDOUT错误。

在最近的内核版本(自2.6.37开始)中,您可以使用TCP_USER_TIMEOUT选项将运行良好。此选项可用于单个插槽。