ssdp 在android上的一些问题 01.07 更新 01.08再次更新

只用MulticastLock是不够的。

先看ubuntu miniDLNA/android BubleUPnP 的抓包例子:

# 192.168.2.103:33940 --> 239.255.255.250:1900
M-SEARCH * HTTP/1.1
Man: "ssdp:discover"
Mx: 3
Host: 239.255.255.250:1900
St: ssdp:all

# 192.168.2.109:1900 --> 192.168.2.103:33940
HTTP/1.1 200 OK
CACHE-CONTROL: max-age=1810
DATE: Mon, 05 Jan 2015 15:27:27 GMT
ST: uuid:4d696e69-444c-164e-9d41-206a8a7025d1
USN: uuid:4d696e69-444c-164e-9d41-206a8a7025d1
EXT:
SERVER: 3.13.0-43-generic DLNADOC/1.50 UPnP/1.0 MiniDLNA/1.1.4
LOCATION: http://192.168.2.109:8200/rootDesc.xml
Content-Length: 0

miniDLNA是开源的C代码,而我只关心android上怎么实现dlna client, 无赖BubleDLNA是闭源的。网上搜了一个例子UpnpDemo[1],核心部分是cling[2]。
而我关心的是里面的AndroidRouter。

# AndroidRouter
 super.lock();
 setWiFiMulticastLock(true); 
 setWifiLock(true);

# RouterImpl.enable()
## lock(writeLock)
 ReentrantReadWriteLock routerLock = new ReentrantReadWriteLock(true);
 Lock readLock = routerLock.readLock();
 Lock writeLock = routerLock.writeLock();
 lock.tryLock(timeoutMilliseconds, TimeUnit.MILLISECONDS);
 lock.unlock();

## createNetworkAddressFactory
import java.net.NetworkInterface;
System.setProperty("java.net.preferIPv4Stack", "true");
NetworkInterface iface \in NetworkInterface.getNetworkInterfaces();
iface.getName() \in System.getProperty(SYSTEM_PROPERTY_NET_IFACES);
iface.getDisplayName(); //wlan0

InetAddress addr = iface.getInetAddresses();
addr.getHostAddress() \in System.getProperty(SYSTEM_PROPERTY_NET_ADDRESSES);
addr instanceof Inet4Address
addr.isLoopbackAddress()
addr.getHostAddress();

InterfaceAddress faddr \in iface.getInterfaceAddresses();
faddr.getBroadcast();
faddr.getAddress() instanceof InetAddress
faddr.getNetworkPrefixLength();

## MulticastReceiver multicastReceiver = createMulticastReceiver()
 ### init
   InetSocketAddress addr = new InetSocketAddress(, port);
   MulticastSocket socket = new MulticastSocket(port);
   socket.setReuseAddress(true);
   socket.setReceiveBufferSize(32768); 
   socket.joinGroup(multicastAddress, faddr);

 ### run
   byte[] buf = new byte[640];
   DatagramPacket datagram = new DatagramPacket(buf, buf.length);
   socket.receive(datagram);

 ### stop
   socket.isClosed();
   socket.leaveGroup(multicastAddress, faddr);
   socket.close();

测试发现上面的receiive已经可以收到udp了。下面的不必要,只是为了完整起见放在这里。

## createStreamServer
  ### init
    localAddress = new InetSocketAddress(addr, 0);
    socket = new MulticastSocket(localAddress);
    socket.setTimeToLive(4);
    socket.setReceiveBufferSize(262144);
  ### run
    byte[] buf = new byte[640];
    DatagramPacket datagram = new DatagramPacket(buf, buf.length);
    socket.receive(datagram);  
    datagram.getAddress().getHostAddress();
    datagram.getPort();
    
## createStreamClient
   ### createRequest http GET/POST


杂项

# isWifi
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); //.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
networkInfo.isAvailable();
networkInfo.isConnected();

NetworkInfo networkInfo.getType():
ConnectivityManager.TYPE_WIFI
ConnectivityManager.TYPE_MOBILE
9 //Ethernet since api 13


代码

 talk is cheap, show me the code -- 根据AndroidRouter写一个最简版的demo:

 https://github.com/DeYangLiu/AndroidPlayer/commit/cb3dd172df8838dd40952342ef0133be1f39d357

测试时不需ndk, 只需把AndroidManifest.xml里面的intent filter注释打开即可。


小结: Android组播需要注意的地方

* InetAddress.getLocalHost()  返回的是 "127.0.0.1"。

* WifiManager.MulticastLock 应该放在joinGroup后面。

# 01.08

感觉说了上面两条没底气,今天来验证了下:

Log.v("gw", "==Inet=" + NetworkInterface.getByInetAddress(InetAddress.getLocalHost()).getDisplayName()); 打印的是lo,当然收不到别人的组播啦。

WifiManager.MulticastLock 放在joinGroup前面也可以的。

更正结论:

* 要选择正确的网络接口,通常是wlan0,才能接收组播数据。

* 组播和广播接收发送为了省电默认是关闭的, 需要用WifiManager.MulticastLock.acquire()打开;由于它耗电,acquire/release圈起来的代码要求尽可能的少。


Ref

[1] http://blog.csdn.net/ljx19900116/article/details/42272095

[2] https://github.com/4thline/cling

你可能感兴趣的:(android,UDP)