Question

I am currently trying to fix a bug in a proxy server I have written relating to the socket select() call. I am using the Poco C++ libraries (using SocketReactor) and the issue is actually in the Poco code which may be a bug but I have yet to receive any confirmation of this from them.

What is happening is whenever a connection abruptly terminates the socket select() call is returning immediately which is what I believe it is meant to do? Anyway, it returns all of the disconnected sockets within the readable set of file descriptors but the problem is that an exception "Socket is not connected" is thrown when Poco tries to fire the onReadable event handler which is where I would be putting the code to deal with this. Given that the exception is silently caught and the onReadable event is never fired, the select() call keeps returning immediately resulting in an infinite loop in the SocketReactor.

I was considering modifying the Poco code so that rather than catching the exception silently it fires a new event called onDisconnected or something like that so that a cleanup can be performed.

My question is, are there any elegant ways of determining whether a socket has closed abnormally using select() calls? I was thinking of using the exception message to determine when this has occured but this seems dirty to me.

Was it helpful?

Solution 2

It appears you are correct Remy. I managed to distinguish whether the socket had disconnected using the following code (this was added to Poco/Net/src/SocketImpl.cpp):

bool SocketImpl::isConnected()
{
int bytestoread;
int rc;
fd_set fdRead;

FD_ZERO(&fdRead);
FD_SET(_sockfd, &fdRead);

struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 250000;

rc = ::select(int(_sockfd) + 1, &fdRead, (fd_set*) 0, (fd_set*) 0, &tv);
ioctl(FIONREAD, &bytestoread);

return !((bytestoread == 0) && (rc == 1));
}

From my understanding, this checks if the socket is readable using a call to select() and then checks the actual number of bytes which are available on that socket. If the socket reports that it is readable but the bytes are 0 then the socket is not actually connected.

While this answers my question here, this unfortunately has not solved my Poco problem as I can't figure out a way to fix this in the Poco SocketReactor code. I tried making a new event called DisconnectNotification but unfortunately cannot call that as the same error gets thrown as does for a ReadNotification on a closed socket.

OTHER TIPS

I had this same problem. The only way to get around it is to control the client applications exit code. The solution that I used was to send a shutdown signal before the reactor was terminated on the client side. Then on the server you simply close the socket.

//Client:
//Handler Class: onWrite
Packet p = Packet::Shutdown();

if (p.fn == "shutdown")
{
    _reactor.stop();
    delete this;
}

//Server
//Accepter Class: onRead
if (p.fn == "shutdown")
{
    printf("%s has disconnected", _username.c_str());
    _socket.close();
    delete this;
}

Just catch the ConnectionResetException in OnReadable() (processes the ReadableNotification) Then it handles "Connection reset by peer" properly.

catch(Poco::Net::ConnectionResetException &ex)
{
    _socket.shutdownSend();
    delete this;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top