Java NIO select()は選択されたキーなしで戻ります-なぜですか?
質問
いくつかのテストコードを書くと、Selector.select()がSelector.selectedKeys()に処理するキーを含まなくても戻ることができることがわかりました。これは、accept()edチャンネルを登録する際のタイトループで発生しています
SelectionKey.OP_READ | SelectionKey.OP_CONNECT
関心のある操作として。
ドキュメントによると、select()は次の場合に返される必要があります。
1)対処できるチャネルがあります。
2)Selector.wakeup()を明示的に呼び出します-キーが選択されていません。
3)select()を実行するスレッドを明示的にThread.interrupt()します-キーは選択されません。
select()の後にキーが表示されない場合は、(2)と(3)のケースにいる必要があります。ただし、私のコードはこれらのリターンを開始するためにwakeup()またはinterrupt()を呼び出していません。
この振る舞いを引き起こしているのは何か?
解決
簡単な回答:受け入れられた接続に関心のある操作のリストから OP_CONNECT
を削除します-受け入れられた接続は既に接続されています。
私は問題を再現することができました。これはまさにあなたに起こっていることかもしれません:
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());
}
}
上記のサーバーが接続を受信すると、接続の最初の select()
はブロックせずに終了し、準備完了操作のあるキーはありません。 Javaがこのように振る舞う理由はわかりませんが、多くの人がこの振る舞いに噛まれているようです。
結果は、Windows XP上のSunのJVM 1.5.0_06とLinux 2.6上のSunのJVM 1.5.0_05および1.4.2_04で同じです。
他のヒント
理由は、 OP_CONNECT
と OP_WRITE
は内部で同じものであるため、両方に同時に登録されることはありません( OP_ACCEPT
および OP_READ
)、およびこの場合のように、チャネルが既に接続されている場合、 OP_CONNECT
に登録されることはありません。
そして OP_WRITE
は、eカーネルのソケット送信バッファーがいっぱいの場合を除いて、ほぼ常に準備ができているため、長さゼロの書き込みを取得した後にのみ登録する必要があります。したがって、すでに接続されているチャンネルを OP_CONNECT
に登録することで、 OP_WRITE
に実際に登録していたので、 select()
がトリガーされました。