2011-09-16 50 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