2011-09-16 49 views
1

先决条件:Android 2.2模拟器。Android套接字上的选择器行为奇怪

我有一个完美工作的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)。切换到阻塞模式的套接字工作正常(所以地址,端口和其他东西都可以)。

好吧,我离开了连接阻塞(现在)。但现在我的接受停止工作 - 选择器不报告传入连接。同样,通过使用阻塞套接字来摆脱Selector的作用。

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

回答

2

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

一旦被添加以下几行,我的代码开始正常工作:

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

I会给你买一瓶啤酒,我会给你买一盒啤酒!我一直在调试我的android应用一整天,它适用于所有网络,但不适用于我母亲的ISP。关于它,从各方面进行测试,但从来没有我可能认为这可能是IPV6没有被ISP使用。我仍然感到困惑,怎么会这样。无论如何,添加这两行解决了这个问题。消息通过! –

6

下面的代码工作于Java

此代码有任何平台上的重大问题。

  1. 您未清除selectedKeySet。通常这是通过遍历它并调用Iterator.remove()完成的,但在这种情况下,您应该调用selectedKeys().clear(),因为您并未这样做,但您确实应该这样做:请参见下文。

  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()超时,如果您想考虑超时,这会很有用。

+0

非常感谢您的意见。由于代码不是我的,选择器是以某种变态的方式设计的,所以理解代码并不容易。但是,您的所有评论适用于从未执行的代码,因为selRes == 0 *立即*。即使我用0替换超时(这应该意味着“无限等待”,代码立即返回。是的,我尝试使用OP_CONNECT而不是validOps() - 并没有帮助。此外,此代码已在桌面上工作多年 –

+1

我已经在我自己的答案中发布了解决方案 –

+1

@ EugeneMayevski'EldoSCorp编写代码的人是真的并不重要,它是*代码*,在这种情况下是'以某种变形的方式设计的',它需要为了符合API规范:它是否解决了这个特定的问题是完全的另一个问题 – EJP