mDNS简介
mdns 即多播(组播)dns(Multicast DNS),用于本地局域网服务发现的协议,在Apple 的设备上(电脑,笔记本,iphone,ipad等设备)都提供了这个服务(Bonjour)。
mDNS有以下几种免费实现方式:
avahi:Linux下实现(http://www.avahi.org/)
jmDNS:JAVA实现(http://jmdns.sourceforge.net/)
Bonjour:MAC OS实现(默认安装)
Bonjour:Windows下实现(https://support.apple.com/kb/DL999?locale=en_US)
本文基于Jmdns开源库实现客户端服务端核心代码
客户端–》服务搜索
JmDNS方法都需要在子线程调用
获取本地IP InetAddress
WifiManager wifiManager = (WifiManager) mContext.getApplicationContext().getSystemService(Context.WIFI_SERVICE);private InetAddress getLocalIpAddress(WifiManager wifiManager) throws UnknownHostException {WifiInfo wifiInfo = wifiManager.getConnectionInfo();int intAddr = wifiInfo.getIpAddress();byte[] byteaddr = new byte[]{(byte) (intAddr & 255),(byte) (intAddr >> 8 & 255),(byte) (intAddr >> 16 & 255),(byte) (intAddr >> 24 & 255)};return InetAddress.getByAddress(byteaddr);}
初始化搜索监听
private class JmdnsListener implements ServiceListener {private MdnsCallback mCallback;public JmdnsListener(MdnsCallback mCallback) {this.mCallback = mCallback;}public void serviceAdded(ServiceEvent ev) {Log.i(TAG, "serviceAdded: ");mJmdns.requestServiceInfo(ev.getType(), ev.getName(), 1);}public void serviceRemoved(ServiceEvent ev) {Log.i(TAG, "serviceRemoved: ");jsonMap.remove(ev.getName());}public void serviceResolved(ServiceEvent ev) {if (!jsonMap.containsKey(ev.getName())) {// 新设备JSONObject jsonObj = toJsonObject(ev.getInfo());Log.i(TAG, "serviceResolved: add");jsonMap.put(ev.getName(), jsonObj);if (mCallback != null) {if (jsonObj == null) {Log.w(TAG, "serviceResolved: jsonObj is null");return;}// 重开线程回调mCallback.onDeviceFind(jsonObj);}}}}/*** mDNS数据格式解析*/private JSONObject toJsonObject(ServiceInfo sInfo) {JSONObject jsonObj;try {jsonObj = new JSONObject();String ipv4 = "";if (sInfo.getInet4Addresses().length > 0) {ipv4 = sInfo.getInet4Addresses()[0].getHostAddress();}// 发现的是被名称,ip 端口信息jsonObj.put("Name", sInfo.getName());jsonObj.put("IP", ipv4);jsonObj.put("Port", sInfo.getPort());byte[] allInfo = sInfo.getTextBytes();int allLen = allInfo.length;byte fLen;for (int index = 0; index < allLen; index += fLen) {fLen = allInfo[index++];byte[] fData = new byte[fLen];System.arraycopy(allInfo, index, fData, 0, fLen);String fInfo = new String(fData, StandardCharsets.UTF_8);if (fInfo.contains("=")) {String[] temp = fInfo.split("=");jsonObj.put(temp[0], temp[1]);}}} catch (Exception e) {e.printStackTrace();jsonObj = null;}return jsonObj;}
加入组播并开始搜索服务
private JmDNS mJmdns;private static final String mServiceName= "_sample._tcp.local.";// 要搜索的服务类型 类似_http._tcp.local.ServiceListener listener = new JmdnsListener(mCallback);InetAddress addr = getLocalIpAddress(wifiManager);mJmdns = JmDNS.create(addr);// step 1mJmdns.addServiceListener(mServiceName, listener); // step 2
结束搜索
mJmdns.removeServiceListener(mServiceName, listener);mJmdns.close();
以上即为客户端搜索服务核心代码
服务端发布服务
注册服务
private final static String REMOTE_TYPE = "_sample._tcp.local.";//注册服务类型,你的发现设备的代码中的type也是这个才能找到这个设备。// step 1 获取本地ip信息InetAddress ip = getLocalIpAddress(wifiManager);// step 2创建jmdns对象JmDNS jmdns = JmDNS.create(ip, "jmdnsSampleName");// jmdns实例名称// step 3发布的服务携带的额外参数final HashMap<String, String> values = new HashMap<String, String>();values.put("test", "vlaue");// step 4创建服务信息类,serverName唯一服务名称(发现端显示服务端的名字),PORT服务端提供建链的端口mServiceInfo = ServiceInfo.create(REMOTE_TYPE, serverName, PORT, 0, 0, values);// step 5 注册服务jmdns.registerService(mServiceInfo);
关闭服务:
jmdns.unregisterService(mServiceInfo);jmdns.close();
搜索端抓包
设备发现: