Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

java

select

nio

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?

like image 231
Frank Taylor Avatar asked Oct 15 '08 09:10

Frank Taylor


People also ask

What is selector in Java NIO?

The Java NIO Selector is a component which can examine one or more Java NIO Channel instances, and determine which channels are ready for e.g. reading or writing. This way a single thread can manage multiple channels, and thus multiple network connections.

What is Java selector?

A selector may be created by invoking the open method of this class, which will use the system's default selector provider to create a new selector. A selector may also be created by invoking the openSelector method of a custom selector provider. A selector remains open until it is closed via its close method.

What is selector threads?

A selector provides a mechanism for monitoring one or more NIO channels and recognizing when one or more become available for data transfer. This way, a single thread can be used for managing multiple channels, and thus multiple network connections.


2 Answers

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.

like image 175
Alexander Avatar answered Nov 06 '22 18:11

Alexander


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 e 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.

like image 41
user207421 Avatar answered Nov 06 '22 19:11

user207421