Reputation: 221
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
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
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
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