Frank Taylor
Frank Taylor

Reputation: 221

Java NIO select() returns without selected keys - why?

In writing some test code I have found that Selector.select() can return without Selector.selectedKeys() containing any keys to process. This is happening in a tight loop when I register an accept()ed channel with

SelectionKey.OP_READ | SelectionKey.OP_CONNECT

as the operations of interest.

According to the docs, select() should return when:

1) There are channels that can be acted upon.

2) You explicitly call Selector.wakeup() - no keys are selected.

3) You explicitly Thread.interrupt() the thread doing the select() - no keys are selected.

If I get no keys after the select() I must be in cases (2) and (3). However, my code is not calling wakeup() or interrupt() to initiate these returns.

Any ideas as to what is causing this behaviour?

Upvotes: 9

Views: 7009

Answers (3)

user207421
user207421

Reputation: 311023

The reason is that OP_CONNECT and OP_WRITE are the same thing under the hood, so you should never be registered for both simultaneously (ditto OP_ACCEPT and OP_READ), and you should never be registered for OP_CONNECT at all when the channel is already connected, as it is in this case, having been accepted.

And OP_WRITE is almost always ready, except when the socket send buffer in the kernel is full, so you should only register for that after you get a zero length write. So by registering the already connected channel for OP_CONNECT, you were really registering for OP_WRITE, which was ready, so select() got triggered.

Upvotes: 10

Pavel Moukhataev
Pavel Moukhataev

Reputation: 319

Upd: added client.finishConnect() as per @user207421 suggestion

You should use OP_CONNECT when you connect to server, not when you are listening for incoming connections. Also make sure to configureBlocking before connect:

    Selector selector = Selector.open();
    SocketChannel serverChannel = SocketChannel.open(StandardProtocolFamily.INET);
    serverChannel.configureBlocking(false);
    serverChannel.connect(new InetSocketAddress("localhost", 5454));
    serverChannel.register(selector, SelectionKey.OP_CONNECT);
    // event process cycle
    { 
        int count = selector.select();
        for (SelectionKey key : selector.selectedKeys()) {
            log.info("    {}", key.readyOps());
            if (key.isConnectable()) {
                boolean connected = client.finishConnect();
                log.info("Connection is ready: {}", connected);
                if (connected) {
                    key.interestOps(SelectionKey.OP_READ);
                }
            }
            if (key.isReadable()) {
                // read data here
            }
    }

Upvotes: -1

Alexander
Alexander

Reputation: 9370

Short answer: remove OP_CONNECT from the list of operations you are interested in for the accepted connection -- an accepted connection is already connected.

I managed to reproduce the issue, which might be exactly what's happening to you:

import java.net.*;
import java.nio.channels.*;


public class MyNioServer {
  public static void main(String[] params) throws Exception {
    final ServerSocketChannel serverChannel = ServerSocketChannel.open();
    serverChannel.configureBlocking(true);
    serverChannel.socket().bind(new InetSocketAddress("localhost", 12345));
    System.out.println("Listening for incoming connections");
    final SocketChannel clientChannel = serverChannel.accept();
    System.out.println("Accepted connection: " + clientChannel);


    final Selector selector = Selector.open();
    clientChannel.configureBlocking(false);
    final SelectionKey clientKey = clientChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_CONNECT);
    System.out.println("Selecting...");
    System.out.println(selector.select());
    System.out.println(selector.selectedKeys().size());
    System.out.println(clientKey.readyOps());
  }
}

After the above server receives a connection, the very first select() on the connection exits without blocking and there are no keys with ready operations. I don't know why Java behaves in this way, but it appears many people get bitten by this behavior.

The outcome is the same on Sun's JVM 1.5.0_06 on Windows XP as well as Sun's JVM 1.5.0_05 and 1.4.2_04 on Linux 2.6.

Upvotes: 10

Related Questions