先决条件: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一起使用?
答案 0 :(得分:6)
以下代码适用于Java
此代码在任何平台上都存在重大问题。
您尚未清除selectedKeySet
。通常这是通过迭代它并调用Iterator.remove()
来完成的,但在这种情况下你应该调用selectedKeys().clear()
,因为你没有这样做,尽管你应该这样做:见下文。
您不应注册interestOps = validOps()。您应该注册OP_CONNECT
,直到finishConnect()
返回true,然后注册OP_READ
或OP_WRITE
,具体取决于您下一步要做什么。
如果连接失败,finishConnect()
会抛出IOException
,您应该关闭该频道。你没有这样做。
如果连接尚未完成,finishConnect()
将返回false,在这种情况下,您应该继续选择。在那时取消密钥没有任何意义。
如果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");