| | | | Browse by category |
I'm using
RWSocket
or RWSocketPortal
low-level operations like recv() and send(), and would like to know how to determine if the other side of my socket closes (i.e. if the peer closes the socket).Cause
For a number of reasons, it is difficult to determine the status of a TCP/IP socket at the other end of the socket. TCP/IP (unless special options like SO_KEEPALIVE are used) does not send any 'watchdog' or 'heartbeat' data from peer-to-peer to make sure the the socket is still considered open by both parties. This makes it fundamentally difficult to detect closure. For example, with normal sockets, if one side of the connection involuntarily stops talking on the socket altogether (i.e. it's power is disconnected), it is not possible for the other side of the connection to know about this (unless logic is written by the programmer to poll the other side to make sure it is alive). However, when sockets are shut down properly, it is possible to determine that they have closed.
Action
With Tools.h++ Professional's version of the recv() and send() calls, it is quite easy to determine when your peer closes the socket. There are two ways that you can receive the notification, either through an EOF when reading if the peer closes the socket properly, or, for all other cases, a socket exception if the other side aborts the connection. (UNIX users may also get a SIGPIPE from their operating system, however, this is highly dependant on the IPC implementation in you OS's kernel and networking components, see the documentation that came with your OS for details on this.) Note also that the code below will work with both blocking and non-blocking sockets.
To check for an EOF when reading with the recv() call, use the alternate forms of the recv() call that allow you to get a RWNetBuf or RWNetBuf::State from the recv() call, for example, instead of:
RWCString buffer;, use this:
buffer = mySocket.recv();
RWNetBuf buffer;
buffer = mySocket.recv();
An RWNetBuf
contains both an RWCString
which contains the data, and a state which tells you if the other side has closed or not. You can test this by treating the RWNetBuf
like a boolean, and you can retrieve the data from it by treating it like an RWCString
:
if(buffer) {
std::cout << The socket is still open, and I got << (RWCString)buffer << from it! << endl;
} else {
std::cout << The peer closed the socket! << endl;
}
The other way that socket closure may be propagted to you is by an exception. Calls such as recv(), send(), and others will throw an exception if the socket reports an error, and this is what can happen if the other side closes the socket improperly. Check for it using C++'s try-catch mechanism:
RWSocketPortal sp = ...This code will catch an exception if the peer closes before/during the send().
...
try {
sp.send(Hello out there!);
} catch (RWxmsg &x) {
cout << Error occured while sending: << x.why() << endl;
}
Also, if you use the select() call provided by Net.h++, and one of the sockets on which you are select()'ing is closed, select() immeaditely returns the RWSocketAttribute for that socket in the list, with it's sock_attr_canread and sock_attr_canaccept (CANREAD and CANACCEPT in previous versions) flags set. Use the variations of the recv() and send() methods described above to read or write to the socket and determine if there is data present or if the socket is closed.