鉴于此前博主发表过dlna开发的相关文章并在github上传了相关工程
github主页-->https://github.com/geniusgithub
亦有不少网友也下载使用了,其中不乏网友反馈说设备找不到或是搜索不稳定云云。。
这里可能原因有很多,下面就博主亲身经历简单阐述下几种可能的原因以及如何排查问题
1.路由环境问题(这种情况较少)
检测手段:下载bubbleupnp(一个很稳定的第三方客户端)
http://www.wandoujia.com/apps/com.bubblesoft.android.bubbleupnp
找两个手机安装下并接入路由器,如果搜索正常则排除此项
否则就是路由器问题,最简单的解决方法就是重启路由器
应该就可以了,如果始终不行就要检查下配置看看是不是设置了防火墙什么的(upnp组播禁用)
2.手机问题
很多手机平板类的移动设备上android系统默认是不打开组播锁的(应用接受组播消息会很耗电),所以需要额外在软件代码里额外加上打开关闭的操作
1)打开权限
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.INTERNET"/>
2.)打开组播锁
WifiManager wifiManager=(WifiManager)context.getSystemService(Context.WIFI_SERVICE); MulticastLock multicastLock=wifiManager.createMulticastLock("MediaRender"); if (multicastLock != null){ multicastLock.acquire(); }
3)关闭组播锁
在退出软件的时候记得关闭
if (mMulticastLock != null){ mMulticastLock.release(); mMulticastLock = null; }
很多不稳定的原因都是因此造成的,在有些设备上甚至会影响到udp单播的接收
对于dmr,dms如果无法接收组播消息则意味着无法响应客户端的searh消息
对于dmp,dmc如果无法接收组播则得不得dmr,dms的上线下线消息,如果连udp单播接收都有问题,那么发出去的search消息即便对方有回应也接收不到,这些都会造成设备找不到或是搜索不稳定的问题
(当然同一个设备上的两个应用之间通讯不存在上述问题)
3.丢包问题
不论是udp组播还是udp单播,在路由器传输过程中都有可能丢包,所以为了尽量避免这种情况,可以在进行组播通知或是搜索的时候多发几次包(比如三次)
当然也不宜过多,否则会造成网络拥塞影响性能,这也引申出另一个问题,若是在网络环境较为复杂的路由器下测试dlna性能是很不理想的(丢包严重)
4.人品问题
简单来说就是丫的两个设备明明没在一个局域网,还嚷嚷着为什么设备找不到
尤其是设备在使用wifi接入条件下,如果wifi不稳定断开了,很可能就连上另一个wifi网了,再说博主的代码有问题LZ就要骂人了
基本上第二点是需要大家注意的,不论是服务端应用还是客户端应用
早些时候可能因为没加上组播锁的缘故,所以会有类似问题,不过写这篇文章的时候
相关code已经commit修改了,请大家下载更新
在反编译了诸如搜狐视频,网易视频等APK发现它们并未添加组播锁,作为dmc就可能导致无法收到dmr的notify组播消息,只能依赖search机制,正如前面所说,部分手机甚至连udp单播都会受影响,那么搜索不到设备也就不奇怪了。
这时候又想使用这些软件怎么办?刚刚给大家推荐了一款软件bubbleupnp,它有一种神奇的魔力,可以打开系统组播锁,让其他应用也能正常接收到组播(LZ至今尚不明白它是怎么做到的,有知道的麻烦告知)
另外附上一个用于组播测试的demo
下载地址:http://download.csdn.net/detail/geniuseoe2012/6847963
截图:
代码片段
public class MulSocketMng { public static interface RecDataCallback{ public void onDataReceive(String hostIP, int port, final String data); } private static final CommonLog log = LogFactory.createLog(); private final static int RECDATA_MSG_ID = 0x0001; private MulticastSocket multicastSocket; private InetAddress mGroup; private int mLocalPort; private boolean isInit = false; private HandlerThread mHandlerThread; private Handler mRecHandler; private RecDataCallback mCallback; public MulSocketMng(){ } public boolean openSokcet(String groupIP, int localPort){ if (isInit){ return true; } try { mLocalPort = localPort; multicastSocket = new MulticastSocket(mLocalPort); multicastSocket.setLoopbackMode(true); mGroup = InetAddress.getByName(groupIP); multicastSocket.joinGroup(mGroup); isInit = true; } catch (Exception e) { e.printStackTrace(); } return isInit; } public void closeSocket(){ if (!isInit){ return ; } try { multicastSocket.leaveGroup(mGroup); multicastSocket.close(); } catch (Exception e) { e.printStackTrace(); } stopListenThead(); isInit = false; } public boolean syncSendData(String data, String targetIP, int targetPort){ if (!isInit){ return false; } SendTask task = new SendTask(data, targetIP, targetPort); Thread thread = new Thread(task); thread.start(); return true; } public boolean startListenThread(RecDataCallback callback){ if (!isInit){ return false; } if (mHandlerThread == null){ startRevThread(); mRecHandler.sendEmptyMessage(RECDATA_MSG_ID); mCallback = callback; } return true; } public void stopListenThead(){ closeRecThread(); } private class SendTask implements Runnable{ private String mData; private String mTargetIP; private int mTargetPort; public SendTask(String data, String targetIP, int targetPort){ mData = data; mTargetIP = targetIP; mTargetPort = targetPort; } @Override public void run() { try { byte []data = mData.getBytes("utf-8"); InetAddress targetAddress = InetAddress.getByName(mTargetIP); DatagramPacket outPacket = new DatagramPacket(data, data.length, targetAddress, mTargetPort); MulticastSocket socket = new MulticastSocket(); log.e("syncSendData mTargetIP = " + mTargetIP + ", TARGET_PORT = " + mTargetPort + "\ncontent --> " + mData); socket.send(outPacket); socket.close(); } catch (Exception e) { e.printStackTrace(); } } private void sendUDP(String data, String ip, int port) { try { byte[] datas = data.getBytes("utf-8"); InetAddress targetAddress = InetAddress.getByName(ip); DatagramPacket packet = new DatagramPacket(datas, datas.length, targetAddress, port); } catch (Exception e) { e.printStackTrace(); } } } private boolean startRevThread(){ if (mHandlerThread != null){ return true; } mHandlerThread = new HandlerThread(""); mHandlerThread.start(); mRecHandler = new Handler(mHandlerThread.getLooper()){ @Override public void handleMessage(Message msg) { switch(msg.what){ case RECDATA_MSG_ID: try { while(true){ revData(); } } catch (IOException e) { e.printStackTrace(); } break; } } }; return true; } private void closeRecThread(){ if (mHandlerThread == null){ return ; } mHandlerThread.quit(); mHandlerThread = null; } private boolean revData() throws IOException { byte[] receiveData = new byte[1024]; DatagramPacket packet = new DatagramPacket(receiveData, receiveData.length); log.e("block to receive packet!!!groupIp = " + mGroup.getHostAddress() + ", port = " + mLocalPort); multicastSocket.receive(packet); String packetIpAddress = packet.getAddress().toString(); packetIpAddress = packetIpAddress.substring(1, packetIpAddress.length()); log.e("rec packet from --> ip: " + packetIpAddress + ", port = " + packet.getPort()); String content = new String(receiveData, "utf-8"); content = content.trim(); log.e("content --> " + content); if (mCallback != null){ mCallback.onDataReceive(packetIpAddress, packet.getPort(), content); } return true; } }
public class MainActivity extends Activity implements OnClickListener, MulSocketMng.RecDataCallback, OnCheckedChangeListener{ private static final CommonLog log = LogFactory.createLog(); private static final String GROUP_IP = "239.255.255.250"; private TextView mTextView; private Button mButtonSend; private Button mButtonOpen; private Button mButtonClose; private Button mButtonClear; private TextView mTVContent; private ScrollView mScrollView; private RadioGroup mRadioGroup; private MulSocketMng mSocketMng; private MulticastLock mMulticastLock; private StringBuffer mDataBuffer = new StringBuffer(); private UnickSocketMng mUnickSocketMng; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); setupViews(); initData(); } @Override protected void onDestroy() { closeWifiBrocast(); super.onDestroy(); } private void setupViews(){ mTextView = (TextView) findViewById(R.id.textview); mButtonSend = (Button) findViewById(R.id.btnSend); mButtonOpen = (Button) findViewById(R.id.btnOpen); mButtonClose = (Button) findViewById(R.id.btnClose); mButtonClear = (Button) findViewById(R.id.btnClear); mButtonClose.setOnClickListener(this); mButtonOpen.setOnClickListener(this); mButtonSend.setOnClickListener(this); mButtonClear.setOnClickListener(this); mTVContent = (TextView) findViewById(R.id.tv_content); mScrollView = (ScrollView) findViewById(R.id.sv_view); mRadioGroup = (RadioGroup) findViewById(R.id.rg_group); mRadioGroup.setOnCheckedChangeListener(this); } private void initData(){ mSocketMng = new MulSocketMng(); mUnickSocketMng = new UnickSocketMng(); } private void updateText(String groupIP, int port){ StringBuffer sBuffer = new StringBuffer(); sBuffer.append("Socket Bind --> groupIP = " + groupIP + ", port = " + port); mTextView.setText(sBuffer.toString()); } private void clearText(){ mTextView.setText("socket close..."); clearContent(); } private void updateContent(String hostIP, int port, final String data){ mDataBuffer.append("rec from ip:" + hostIP + ", port = " + port); mDataBuffer.append("\n" + data + "\n-----------------\n"); mTVContent.setText(mDataBuffer.toString()); mScrollView.scrollTo(0, 1024 * 1024); } private void clearContent(){ mDataBuffer = new StringBuffer(); mTVContent.setText(""); } @Override public void onClick(View v) { switch(v.getId()){ case R.id.btnClose: close(); break; case R.id.btnOpen: open(); break; case R.id.btnSend: send(); break; case R.id.btnClear: clearContent(); break; } } private static final int TARGET_PORT = 1900; private void send(){ String value = "test mulbrocast!!!"; boolean ret = mSocketMng.syncSendData(value, GROUP_IP, TARGET_PORT); mUnickSocketMng.syncSendData("udpdata..", "192.168.11.3", 12345); } private static final int LOCAL_PORT = 1900; private void open(){ boolean ret = mSocketMng.openSokcet(GROUP_IP, LOCAL_PORT); log.e("openSokcet GROUP_IP = " + GROUP_IP + ", LOCAL_PORT = " + LOCAL_PORT + ", ret = " + ret); mSocketMng.startListenThread(this); updateText(GROUP_IP, LOCAL_PORT); mUnickSocketMng.openSokcet(12345); mUnickSocketMng.startListenThread(); } private void close(){ mSocketMng.closeSocket(); log.e("closeSocket"); clearText(); mUnickSocketMng.closeSocket(); } @Override public void onDataReceive(final String hostIP,final int port, final String data) { runOnUiThread(new Runnable() { @Override public void run() { updateContent(hostIP, port, data); } }); } @Override public void onCheckedChanged(RadioGroup group, int checkedId) { switch(checkedId){ case R.id.rb_open: openWifiBrocast(); break; case R.id.rb_close: closeWifiBrocast(); break; } } private void openWifiBrocast(){ if (mMulticastLock == null){ WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); mMulticastLock = wifiManager.createMulticastLock("multicast.test"); if (mMulticastLock != null){ mMulticastLock.acquire(); log.e("openWifiBrocast"); } } } private void closeWifiBrocast(){ if (mMulticastLock != null){ mMulticastLock.release(); mMulticastLock = null; log.e("closeWifiBrocast"); } } }
绑定和发送的组播地址是239.255.255.250:1900,正是upnp组播地址
测试的时候软件要装两个手机上,测udp单播的话改下目标IP即可
Ok本文到此为止~-~
more brilliant,Please pay attention to my CSDN blog -->http://blog.csdn.net/geniuseoe2012