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 1
mJmdns.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();
搜索端抓包
设备发现: