How to detect a TCP socket disconnection (with C Berkeley socket)
I am using a loop to read message out from a c Berkeley socket but I am not able to detect when the socket is disconnected so I would accept a new connection. please help
while(true) {
bzero(buffer,256);
n = read(newsockfd,buffer,255);
printf("%s\n",buffer开发者_StackOverflow);
}
The only way you can detect that a socket is connected is by writing to it.
Getting a error on read()/recv()
will indicate that the connection is broken, but not getting an error when reading doesn't mean that the connection is up.
You may be interested in reading this: http://lkml.indiana.edu/hypermail/linux/kernel/0106.1/1154.html
In addition, using TCP Keep Alive may help distinguish between inactive and broken connections (by sending something at regular intervals even if there's no data to be sent by the application).
(EDIT: Removed incorrect sentence as pointed out by @Damon, thanks.)
Your problem is that you are completely ignoring the result returned by read()
. Your code after read()
should look at least like this:
if (n == 0) // peer disconnected
break;
else if (n == -1) // error
{
perror("read");
break;
}
else // received 'n' bytes
{
printf("%.*s", n, buffer);
}
And accepting a new connection should be done in a separate thread, not dependent on end of stream on this connection.
The bzero()
call is pointless, just a workaround for prior errors.
That's because you didn't use keepalive timeout. In receiving side, keepalive socket option is the best solution for detecting dead connection.
But, in case of your application continue to write to socket, there is something to think more. Even though you already set keepalive option to your application socket, you can't detect in time the dead connection state of the socket, in case of your app keeps writing on the socket. That's because of tcp retransmission by the kernel tcp stack. tcp_retries1 and tcp_retries2 are kernel parameters for configuring tcp retransmission timeout. It's hard to predict precise time of retransmission timeout because it's calculated by RTT mechanism. You can see this computation in rfc793. (3.7. Data Communication)
https://www.rfc-editor.org/rfc/rfc793.txt
Each platforms have kernel configurations for tcp retransmission.
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
You should set lower value for tcp_retries2 (default 15) if you want to early detect dead connection, but it's not precise time as I already said. In addition, currently you can't set those values only for single socket. Those are global kernel parameters. There was some trial to apply tcp retransmission socket option for single socket(http://patchwork.ozlabs.org/patch/55236/), but I don't think it was applied into kernel mainline. I can't find those options definition in system header files.
For reference, you can monitor your keepalive socket option through 'netstat --timers' like below. 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)
In addition, when keepalive timeout ocurrs, you can meet different return events depending on platforms you use, so you must not decide dead connection status only by return events. For example, HP returns POLLERR event and AIX returns just POLLIN event when keepalive timeout occurs. You will meet ETIMEDOUT error in recv() call at that time.
In recent kernel version(since 2.6.37), you can use TCP_USER_TIMEOUT option will work well. This option can be used for single socket.
精彩评论