Cancel a selection key but reuse it afterwards

I want to register a channel to a selector at first to perform select() on connect only and afterwards on write only.

something like this:

SelectionKey key = server.register(selector, SelectionKey.OP_CONNECT);
server.connect(address);
int readyChannels = 0;
while (readyChannels == 0) readyChannels = selector.select(config.connectTimeout);
server.finishConnect();
key.cancel();

key = server.register(selector, SelectionKey.OP_WRITE);
// write something to the channel and check timeout 
// with selector.select(config.writeTimeout)

However, when I reach the 2nd register(...) in the code I get a CancelledKeyException.

Is there some other way to do this?

Is the right way to do it is to define a new selector every time?

P.S. I know I can use key.isWritable() and such.

Answer

  1. If finishConnect() returns false it isn’t correct to proceed as though it had returned true.

  2. Your problem is that you aren’t calling the selector in between the cancel and the register, so there is unfinished business. The simplest solution here is to remove the cancel and the register and just adjust the interestOps of the existing selection key directly.

  3. However as you are just effectively blocking until the connect completes, it would be simpler to do the connect in blocking mode, and go to non-blocking afterwards, for the subsequent I/O:

    // Still in blocking mode, no register() yet ...
    server.socket().connect(address, connectTimeout);
    server.configureBlocking(false);
    key = server.register(selector, SelectionKey.OP_WRITE);
    

    … noting that if the timeout expires, an exception is thrown.

I’ve rarely encountered a case in which it makes any sense to use NIO in non-blocking mode at all in a client, actually.

Leave a Reply

Your email address will not be published. Required fields are marked *