Java NIO select() 返回时没有选择键 - 为什么?

问题描述 投票:0回答:3

在编写一些测试代码时,我发现 Selector.select() 可以返回,而 Selector.selectedKeys() 不包含任何要处理的键。当我用

注册一个接受()ed通道时,这是在一个紧密的循环中发生的
SelectionKey.OP_READ | SelectionKey.OP_CONNECT

作为感兴趣的操作。

根据文档, select() 应该在以下情况下返回:

1) 有可以采取行动的渠道。

2)您显式调用 Selector.wakeup() - 没有选择任何键。

3)您明确 Thread.interrupt() 执行 select() 的线程 - 没有选择任何键。

如果我在 select() 之后没有得到任何键,我一定是在情况 (2) 和 (3) 中。但是,我的代码没有调用wakeup()或interrupt()来启动这些返回。

关于导致这种行为的原因有什么想法吗?

java select nio
3个回答
10
投票

简短回答:从您对接受的连接感兴趣的操作列表中删除

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 的结果相同。


10
投票

原因是

OP_CONNECT
OP_WRITE
本质上是同一件事,因此您永远不应该同时注册两者(同上
OP_ACCEPT
OP_READ
),并且您永远不应该注册
OP_CONNECT
当通道已经连接时,就像在本例中一样,已被接受。

并且

OP_WRITE
几乎总是准备就绪,除非内核中的套接字发送缓冲区已满,所以您应该只在获得零长度写入后进行注册。因此,通过注册已连接的
OP_CONNECT,
通道,您实际上是在注册已准备就绪的
OP_WRITE,
,因此
select()
被触发。


-1
投票

更新:根据 @user207421 建议添加了 client.finishConnect()

您应该在连接到服务器时使用

OP_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 } }


© www.soinside.com 2019 - 2024. All rights reserved.