7

在编写一些测试代码时,我发现 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() 来启动这些返回。

关于是什么导致这种行为的任何想法?

4

2 回答 2

9

简短的回答: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 上的结果是相同的。

于 2008-10-15T16:13:21.870 回答
9

原因是OP_CONNECTOP_WRITE本质上是一样的,所以你不应该同时注册两者(同上OP_ACCEPTOP_READ),当通道已经连接时,你根本不应该注册OP_CONNECT,就像在这种情况下一样,已被接受。

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

于 2011-07-11T05:32:05.060 回答