安卓:Selector.select() 立即返回0

3
我正在编写一款 Android 应用程序(版本号>=2.3),它连接到服务器,但我在使用 NIO 选择器时遇到了问题。
Selector selector = SelectorProvider.provider().openSelector();
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);

selector.select();  // returns 0 immediately

根据Android API文档,select()方法不会返回直到至少有一个通道准备好、wakeup()被调用或者调用线程被中断。如果没有以上任何情况发生但是它返回了,那么select()方法的行为与规范描述不符,我认为这是一个bug。 编辑 根据EJP在下面的评论,我已经修改了代码。
socketChannel.register(selector, SelectionKey.OP_CONNECT);
socketChannel.connect(serverAddress);

while (true) {
  selector.select();

  for (SelectionKey key: _selector.selectedKeys()) {
    if (key.isConnectable())
      finishConnect(key);
    ...
  }
  selector.selectedKeys().clear();
}

当服务器正在运行时,它可以工作:select()会返回1个已选择的键,我可以调用finishConnect()来建立连接。但是如果服务器未运行,则连接被拒绝,select()将返回0个已选择的键。如果没有已选择的键,则无法确定下一步该做什么。(再次说明,这种情况不属于API文档中描述的select()返回的3种类别之一。)

1个回答

0

在注册套接字之前,请尝试将其连接到某个东西。可以在将其设置为非阻塞模式之前这样做,或者在此之后执行此操作,但是注册OP_CONNECT而不是OP_READ,并在其触发时调用finishConnect():如果它返回true,则注销OP_CONNECT并注册OP_READ。


谢谢回复。如果服务器宕机且连接被拒绝,该怎么办?这种情况下select()返回0并且selectedKeys()为空,那我怎么知道连接失败并处理这种情况呢? - K J
问题在于当连接失败时,select() 返回而没有正确设置 selectedKeys()。即使连接失败,也应该选择键,以便随后的 finishConnect() 调用可以捕获异常以知道它失败了。这是 Java 文档中指定的 OP_CONNECT 的行为,如果 Android 不遵循它,那么我认为这是一个错误。 - K J
你没有理解重点。在上面的代码中,你在连接套接字之前调用了select *。这是前后颠倒的。isConnectable()发生在你调用connect()之后,并且它是一个信号,告诉你应该尝试调用finishConnect()。它不是一个信号,告诉你现在可以调用connect()。你不需要等待任何东西。 - user207421
不,我的意思是,我根据您的建议更改了代码,在服务器运行时它可以工作,但如果连接被拒绝,select()仍然返回0个选择的键。我已经更新了问题。 - K J
我的观点是,如果它的行为与文档描述的不同,那么这就是一个 bug。select() 不应该只是返回,即使我使用不当。如果我传递了错误的参数,它应该抛出异常而不是随机返回。或者至少应该像“如果在连接未建立时使用 OP_READ 调用,则行为未定义”之类的内容进行记录。 - K J
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接