preloader
軟體工程

Android 4.2 Jellybean Wi-Fi Direct無法使用UDP broadcast

據本人實驗結果,Android 4.2 JellyBean Wi-Fi Direct無法送UDP broadcast到遠端Android設備,也沒收到遠端設備發過來的udp broadcast封包。

  1. 測試條件環境如下:
  • 測試位址

    • 192.168.49.255。 翻了Android Wi-Fi Direct相關原始碼,發現它用DHCP方式配發IP,範圍落在192.168.49.2 到 192.168.49.254,且觀察到Wi-Fi Direct網路卡配發到的位址常是 192.168.49.2/24、192.168.49.3/24和192.168.49.49/24,所以用192.168.49.255是正常的。
    • 0.0.0.0。發送到本機所有網路卡和從所有本機網路卡接收資料。
    • 255.255.255.255。也是一個區網廣播位址,執行程式時會有錯誤訊息。
    • 192.168.49.0。因為Wi-Fi Direct使用的DHCP server在192.168.49.1,於是跳過192.168.49.1,直接測這個位址,雖然測之前我覺得沒必要測這個,事實證明真的沒用到。
  • 測試封包量大小

    • 8192 bytes
    • 768 bytes
    • 417 bytes。以上這三個也是遠端設備無法收到,雖然我從stackoverflow.com看到Java 1.6可以到 4KBytes, Android用Java 1.5應該是差沒多少才對。
  • 測試WifiManager class的MulticastLock

    • 開啟
    • 關閉。有沒有開都沒影響收不到對方發過來的事實。
  • 測試有無設定DatagramSocket的receiveBuffer size

    • 有設定

      • 81920 Bytes
      • 768 Bytes
      • 417 Bytes。不論設多少,都只能收到本機發給位址0.0.0.0的broadcast封包,由於我是發聲音的資料封包,可藉由聽播放的聲音時間長短,約略得知資料量的差異。聽起來播放出來的時間長度都一樣。設417 Bytes這麼小是因為參考Java網路程式設計中文版第三版的建議,封包尺寸設成500Bytes以下。
    • 無設定

      • 收不到資料。

 

測試流程是排列組合這四大項環境條件,都是收不到對方設備發過來的聲音資料。

 

我找了很多stackoverflow.com的資料和網路上其他地方的資料(包括Android在code.google.com的issue論壇),發現舊版本的有些 Android 設備(Android 4.x以前版本)是可以用 WiFi 發送和接收udp broadcast或multicastSocket資料,這也僅是舊版本的有些設備,其他的設備依然是不能收送udp broadcast或multicastSocket資料。我照著他們的解決方法改寫程式碼,在測試機器上依然是不能做到跟他們一樣。所以我懷疑,Android系統本身可能延續2.x版本的Bug,且Android 4.0之後的 Wi-Fi Direct 網路介面,根本非常陽春,只能支援TCP的Socket,也就是Java的Socket和ServerSocket class。

 

  1. UDP broadcast測試程式碼,執行時放在不同的區塊
//Send data

DatagramSocket send_socket = new DatagramSocket(8988)

send_socket.setBroadcast(true)

byte[] buffer = new byte[SIZE]

// fill data...skip

// fill data...skip

SocketAddress groupAddress = new SocketAddress(InetAddress.getByName("0.0.0.0"), 8989)

DatagramPacket send_datagram = new DatagramPacket(buffer, buffer.length, groupAddress)

send_socket.send(send_datagram)





//Receive data

DatagramSocket recv_socket = new DatagramSocket(8989)

recv_socket.setBroadcast(true)

byte[] recv_buffer = new byte[SIZE]

DatagramPacket recv_datagram = new Datagram(recv_buffer, recv_buffer.length)

recv_socket.receive(recv_datagram)

// get received data ... skip