Android套接字上的选择器表现奇怪

时间:2011-09-16 21:52:34

标签: java android nio

先决条件:Android 2.2模拟器。

我有一个完美的Java代码,它也可以完美地为Android编译。但是有一个奇怪的部分。特别是,似乎java.nio.Selector根本不起作用。

连接期间出现第一个问题。以下代码适用于Java,但不适用于Android(有关详细信息,请参见下文)。

socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress(remoteAddr, getRemotePort()));

Selector selector = Selector.open();
socketChannel.register(selector, socketChannel.validOps());

// Wait for an event
int selRes = selector.select(timeout); 
if (selRes == 1)
{
    SelectionKey selKey = (SelectionKey)selector.selectedKeys().iterator().next();
    if (selKey.isValid() && selKey.isConnectable()) {
        // Get channel with connection request
        boolean success = socketChannel.finishConnect();
        if (!success) {
            selKey.cancel();
        }
    }
}                   

我传递超时30000(毫秒,即30秒),但选择立即返回selres等于0(在桌面Java上它是1)。将套接字切换到阻塞模式工作正常(因此地址,端口和其他东西都可以)。

好的,我保持连接阻塞(暂时)。但现在我的Accept停止工作 - Selector不会报告传入的连接。再次,使用阻塞套接字摆脱Selector工作。

所以问题是 - Selector在Android中是否可以工作,或者应该重写代码以避免Selector和java.nio一起使用?

2 个答案:

答案 0 :(得分:6)

  

以下代码适用于Java

此代码在任何平台上都存在重大问题。

  1. 您尚未清除selectedKeySet。通常这是通过迭代它并调用Iterator.remove()来完成的,但在这种情况下你应该调用selectedKeys().clear(),因为你没有这样做,尽管你应该这样做:见下文。

    < / LI>
  2. 您不应注册interestOps = validOps()。您应该注册OP_CONNECT,直到finishConnect()返回true,然后注册OP_READOP_WRITE,具体取决于您下一步要做什么。

  3. 如果连接失败,finishConnect()会抛出IOException,您应该关闭该频道。你没有这样做。

  4. 如果连接尚未完成,finishConnect()将返回false,在这种情况下,您应该继续选择。在那时取消密钥没有任何意义。

  5. 如果selres > 1您根本没有处理任何选定的密钥。测试应该是if (selRes > 0),并不是真的有必要,因为迭代selectedKeySet只会迭代零次;但selRes == 0确实表示select()超时,如果您想考虑超时,这可能很有用。

答案 1 :(得分:3)

这个问题在Android bug跟踪器中看似无关的错误报告中找到了一个奇怪的解决方案。 Android Emulator不支持IPv6,虽然我不假装请求IPv6,但默认情况下Selector似乎尝试在IPv6堆栈上工作。

添加以下行后,我的代码开始正常运行:

java.lang.System.setProperty("java.net.preferIPv4Stack", "true");
java.lang.System.setProperty("java.net.preferIPv6Addresses", "false");