最近,想做一個跨平臺的區域網的文件傳輸軟體,思路是組播設備信息,TCP連接傳輸文件。於是進行了一次簡單的UDP組播測試,發現Android對於UDP組播接收數據的支持即極為有限。 部分代碼如下 1 package com.hsocket.Udp; 2 3 import java.io.IOExcep ...
最近,想做一個跨平臺的區域網的文件傳輸軟體,思路是組播設備信息,TCP連接傳輸文件。於是進行了一次簡單的UDP組播測試,發現Android對於UDP組播接收數據的支持即極為有限。
部分代碼如下
1 package com.hsocket.Udp; 2 3 import java.io.IOException; 4 import java.net.DatagramPacket; 5 import java.net.DatagramSocket; 6 7 public class UdpReceiver { 8 protected DatagramSocket client=null; 9 private OnReceiveListener mOnReceiveListener=null; 10 private Thread thrRecv=null; 11 protected int port=0; 12 public UdpReceiver(int port){ 13 this.port=port; 14 } 15 protected DatagramSocket Create() throws IOException{ 16 return new DatagramSocket(this.port); 17 } 18 public void addOnReceiveListener(OnReceiveListener mOnReceiveListener){ 19 this.mOnReceiveListener=mOnReceiveListener; 20 } 21 public void Stop(){ 22 if(this.thrRecv!=null) this.thrRecv.interrupt(); 23 this.Close(); 24 } 25 public void Listen() throws IOException{ 26 this.Close(); 27 this.client=this.Create(); 28 if(this.thrRecv!=null) this.thrRecv.interrupt(); 29 this.thrRecv=new Thread(new Runnable() { 30 @Override 31 public void run() { 32 while(!Thread.interrupted()){ 33 ReceiveEventArgs args=new ReceiveEventArgs(); 34 try { 35 DatagramPacket packet=UdpReceiver.this.Receive(); 36 args.Address=packet.getAddress(); 37 args.Result=packet.getData(); 38 args.Length=packet.getLength(); 39 args.Error=false; 40 } catch (IOException e) { 41 e.printStackTrace(); 42 args.Exception=e; 43 args.Error=true; 44 } 45 UdpReceiver.this.OnReceive(args); 46 if(UdpReceiver.this.mOnReceiveListener!=null) 47 UdpReceiver.this.mOnReceiveListener.OnReceive(UdpReceiver.this, args); 48 } 49 } 50 }); 51 this.thrRecv.start(); 52 } 53 protected DatagramPacket Receive() throws IOException{ 54 byte[] recvBuf = new byte[4096]; 55 DatagramPacket recvPacket= new DatagramPacket(recvBuf , recvBuf.length); 56 this.client.receive(recvPacket); 57 return recvPacket; 58 } 59 protected void Close(){ 60 if(this.client!=null) this.client.close(); 61 } 62 protected void OnReceive(ReceiveEventArgs args){ 63 64 } 65 }View Code
1 package com.hsocket.Udp; 2 3 import java.io.IOException; 4 import java.net.DatagramSocket; 5 import java.net.InetAddress; 6 import java.net.MulticastSocket; 7 8 public class UdpMultcastReceiver extends UdpReceiver { 9 10 private InetAddress multicastAddr=null; 11 public UdpMultcastReceiver(InetAddress multicastAddr,int port) { 12 super(port); 13 this.multicastAddr=multicastAddr; 14 } 15 16 @Override 17 protected DatagramSocket Create() throws IOException { 18 MulticastSocket socket=new MulticastSocket(this.port); 19 socket.joinGroup(this.multicastAddr); 20 socket.setLoopbackMode(false); 21 return socket; 22 } 23 }View Code
發現UDP組播接收數據在部分機型存在問題,與系統有極大關係。小米、華為的手機的深度定製系統對UDP的封殺極為嚴重。
首先是組播鎖,Android的Wifi,預設情況下是不接受組播的,見:http://developer.android.com/reference/android/net/wifi/WifiManager.MulticastLock.html
要想打開組播功能,有以下幾個步驟:
- 在Manifest文件中加入:android.permission.CHANGE_WIFI_MULTICAST_STATE,這個許可權
- 獲取到MulticastLock對象,這個對象不能直接實例化,要通過WifiManager間接得到,工廠模式
- 調用MulticastLock對象的acquire方法,獲取到組播鎖
- 相應的,用完組播,為了不浪費電力,要調用MulticastLock的release方法釋放鎖
WifiManager wifiManager=(WifiManager)getSystemService(Context.WIFI_SERVICE); multicastLock=wifiManager.createMulticastLock("multicast.test"); multicastLock.acquire();
其次,即使獲取到組播鎖,但部分機型依舊無法接收到廣播,因為部分Android有多網卡,預設是迴環外卡,IP為127.0.0.1,無法加入組播,需用MulticastSocket::setNetworkInterface來設置組播網卡
mMulticastSocket.setNetworkInterface(NetworkInterface.getByName("wlan0"));