PDA

View Full Version : 2.1 - How to tell when a socket is closed on the other end?


Loco
07-24-2002, 05:29 PM
This text has been taken from the original FAQ.

2.1 - How can I tell when a socket is closed on the other end?
From Andrew Gierth ( andrew@erlenstar.demon.co.uk):

AFAIK:

If the peer calls close() or exits, without having messed with SO_LINGER, then our calls to read() should return 0. It is less clear what happens to write() calls in this case; I would expect EPIPE, not on the next call, but the one after.

If the peer reboots, or sets l_onoff = 1, l_linger = 0 and then closes, then we should get ECONNRESET (eventually) from read(), or EPIPE from write().

I should also point out that when write() returns EPIPE, it also raises the SIGPIPE signal - you never see the EPIPE error unless you handle or ignore the signal.

If the peer remains unreachable, we should get some other error.

I don't think that write() can legitimately return 0. read() should return 0 on receipt of a FIN from the peer, and on all following calls.

So yes, you must expect read() to return 0.

As an example, suppose you are receiving a file down a TCP link; you might handle the return from read() like this:


rc = read(sock,buf,sizeof(buf));
if (rc > 0)
{
write(file,buf,rc);
/* error checking on file omitted */
}
else if (rc == 0)
{
close(file);
close(sock);
/* file received successfully */
}
else /* rc < 0 */
{
/* close file and delete it, since data is not complete
report error, or whatever */
}



From: Andrew Maholski
This answer does not really tell what to do if you wish to check a socket prior to your read attempt. If the server requires you to maintain an open socket but will time out after a period of inactivity you should use select with a short timeout to determine the socket status. The following code demonstrates this:

int Socket_Status(int sock, int to_secs, int to_msecs)
{

int sval;
int ret_val = 1;
fd_set check_set;
struct timeval to;



FD_ZERO(&check_set);
FD_SET(sock,&check_set);

to.tv_sec = to_secs;
to.tv_usec = to_msecs;

if((sval = select(sock + 1,&check_set,0,0,&to)) < 0)
{
logprintf(LOG_ERRORS,"Select returned %d %d",sval,errno);
ret_val = -1;
}
if(sval == 0)
{
logprintf(LOG_EVERYTHING,"Select timed out.");
ret_val = 0;
}
else if(sval > 0)
logprintf(LOG_EVERYTHING,"Select shows %d as return val.",sval);

return(ret_val);

}


call this routine with:


if(Socket_Status(sock,0,1))
{
logprintf(LOG_EVERYTHING,"The socket is not up.");
/*close your end of the thing*/
}


From: Michael Lampkin
Added on: 2002-05-31 05:04:43

Hmmmm... again...

Re-reading some of the older posts... I never have read the entire faq in its current format... anyway... on this topic...

Personally I will have to stick with using a read(...) call even if I do NOT want to read data and instead just want to check on the status of a socket...

The reason is that using select as given in the second example:

1) you will have to create a local fd_set or maintain a global one for the purpose == extra memory used.

2) you will have to clear the fd_set prior to each call == extra cpu cycles.

3) most (all?) OSes have a timer interval "significantly" higher than the smallest time wait val select will take meaning you code will "stall" for longer than expected in the select call.

Instead... how about this...:

rc = read(sock, NULL, 0);

On a good connection, it will return 0 (yes, read will return 0 on call where you tell it to read 0 bytes, not just when you get EOF)... and a -1 and errno set otherwise...

Just a thought but this seems to save you the hassles outlined in the select method...

Of course, the entire situation seems to be a bit of an academic exercise... since in a thread per socket design you can just block on read(...) and will know immediately when there is an error (ret -1 w/ errno set)... and on a multiple / all sockets in a single process or thread you would be calling select (poll / whatever) anyway and watching ALL (instead of selecting on each one individually) currently open sockets... and again would catch the error(s) quickly...

ML


From: Michael Lampkin
Added on: 2002-05-31 05:12:24

Oops... quick addition...

Using the read method... you probably SHOULD still check for an errno of EINTR (and EAGAIN / EWOULDBLOCK if the socket is in non-blocking mode) on a return of -1 ... instead of assuming that the -1 return indicates the presence of a "hard" error...

But... the truth is...

On a read( ) call with the size set to 0, the system should not return those errnos if the system is implemented properly / in a reasonable / sane manner... always better safe than sorry though...

ML


From: Rob Seace
Added on: 2002-05-31 09:26:03

Interesting... But, that would only let you spot actual
errors in the connection, and not a normal close of the
connection, right? (Ie: there's no way to distinguish
the 0 return because that's how much you asked to read
from the 0 return indicating EOF/FIN, is there?) If all
you're after is the current error state of the connection,
wouldn't getsockopt() of SO_ERROR suffice, in a much more
straight-forward manner?

However, if you're looking for a way to tell if the
connection is actually closed, I think you'll need to try
reading at least 1 byte (you can use recv() with MSG_PEEK,
if you don't want to really read it)... Or, alternatively,
you could check if it select()'s as readable, but there are
zero bytes waiting to be read (ioctl() of FIONREAD)...


From: Michael Lampkin
Added on: 2002-05-31 13:03:07

Yup, you can get a close and the 0's match making it a bit ambiguous but the thought was if you were periodically polling for state... you would get it on the next pass...

I was going to suggest to use getsockopt with SO_ERROR but figured it have the same "2nd" call requirement as the read? The catch is, you can get an error on a socket while there is still valid data in the buffers that has not be read yet... e.g. a peer writes data and immediately disconnects prior to your first read... and there were also concerns about the fact that there are 2 different impls of it out there (return the error in the function return value and return in the flags param)...

Either way... yup... its just as good... so write it up with the warnings :-P

ML