Java – Detecting socket disconnection

javaniosockets

I am kinda upset that this cannot be handled in an elegant way, after trying different solutions (this, this and several others) mentioned in answers to several SO questions, I still could not manage to detect socket disconnection (by unplugging cable).

I am using NIO non-blocking socket, everything works perfectly except that I find no way of detecting server disconnection.

I have the following code:

while (true) {
    handlePendingChanges();

    int selectedNum = selector.select(3000);
    if (selectedNum > 0) {
        SelectionKey key = null;
        try {
            Iterator<SelectionKey> keyIterator = selector.selelctedKeys().iterator();
            while (keyIterator.hasNext()) {
                key = keyIterator.next();
                if (!key.isValid())
                    continue;

                System.out.println("key state: " + key.isReadable() + ", " + key.isWritable());

                if (key.isConnectable()) {
                    finishConnection(key);
                } else if (key.isReadable()) {
                    onRead(key);
                } else if (key.isWritable()) {
                    onWrite(key);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("I am happy that I can catch some errors.");
        } finally {
            selector.selectedKeys().clear();
        }
    }
}

While the SocketChannels are being read, I unplug the cable, and Selector.select() starts spinning and returning 0, now I have no chance to read or write the channels, because the main reading & writing code is guarded by if (selectedNum > 0), now this is the first confusion coming out of my head, from this answer, it is said that when the channel is broken, select() will return, and the selection key for the channel will indicate readable/writable, but it is apparently not the case here, the keys are not selected, select() still returns 0.

Also, from EJP's answer to a similar question:

If the peer closes the socket:

  • read() returns -1
  • readLine() returns null
  • readXXX() throws EOFException, for any other X.

Not the case here either, I tried commenting out if (selectedNum > 0) and using selector.keys().iterator() to get all the keys regardless whether or not they are selected, reading from those keys does not return -1 (0 returned instead), and writing to those keys does not get EOFException thrown. I only noted one thing, that even the keys are not selected, key.isReadable() returns true while key.isWritable() returns false (I guess this might be because I didn't register the keys for OP_WRITE).

My question is why Java socket is behaving like this or is there something I did wrong?

Best Answer

You have discovered that you need timers, and heartbeat on a TCP connection.

If you unplug the network cable, the TCP connection may not be broken. If you have nothing to send, the TCP/IP stack have nothing to send, it doesn't know that a cable is gone somewhere, or that the peer PC suddenly burst into flames. That TCP connection could be considered open until you reboot your server years later.

Think of it this way; how can the TCP connection know that the other end dropped off the network - it's off the network so it can't tell you that fact.

Some systems can detect this if you unplug the cable going into your server, and some will not. If you unplug the cable at the other end of e.g. an ethernet switch, that will not be detected.

That's why one always need supervisor timers(that e.g. send a heartbeat message to the peer, or close a TCP connection based on no activity for a given amount of time) for a TCP connection,

One very cheap way to at least avoid TCP connections that you only read data from, never write to, to stay up for years on end, is to enable TCP keepalive on a TCP socket - be aware that the default timeouts for TCP keepalive is often 2 hours.

Related Topic