Android - jmdns не обнаруживает устройства
Я пытаюсь реализовать класс для обнаружения служб в сети. Я пытался работать с NSD Android, и он действительно обнаруживает службы, но поддерживает только API-уровни 16 и выше, и я не могу получить поле txtRecord в информации службы (по какой-то причине он возвращает ноль). Оказывается, это известная проблема...
Поэтому сейчас я пытаюсь работать с jmDNS, который, похоже, вообще не находит сервисов. вот мой класс (я работаю с платформой AndroidAnnotations) MDnsHelper:
@EBean
public class MDnsHelper implements ServiceListener {
public static final String SERVICE_TYPE = "_http._tcp.local";
Activity activity;
private JmDNS jmdns;
private MulticastLock multicastLock;
WifiManager wm;
InetAddress bindingAddress;
boolean isDiscovering;
public void init(Activity activity) {
this.activity = activity;
isDiscovering = false;
wm = (WifiManager) activity.getSystemService(Context.WIFI_SERVICE);
multicastLock = wm.createMulticastLock(activity.getPackageName());
multicastLock.setReferenceCounted(false);
}
@Background
public void startDiscovery() {
if (isDiscovering)
return;
System.out.println("starting...");
multicastLock.acquire();
try {
System.out.println("creating jmdns");
jmdns = JmDNS.create();
System.out.println("jmdns created");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (jmdns != null) {
jmdns.addServiceListener(SERVICE_TYPE, MDnsHelper.this);
isDiscovering = true;
System.out.println("discovering services of type: " + SERVICE_TYPE);
}
}
}
@Background
public void stopDiscovery() {
if (!isDiscovering || jmdns == null)
return;
System.out.println("stopping...");
multicastLock.release();
jmdns.removeServiceListener(SERVICE_TYPE, MDnsHelper.this);
System.out.println("listener for " + SERVICE_TYPE + " removed");
try {
jmdns.close();
isDiscovering = false;
System.out.println("jmdns closed");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void serviceAdded(ServiceEvent service) {
System.out.println("found: " + service.getInfo().toString());
}
@Override
public void serviceRemoved(ServiceEvent service) {
System.out.println("lost: " + service.getInfo().toString());
}
@Override
public void serviceResolved(ServiceEvent service) {
System.out.println("resolved: " + service.getInfo().toString());
}
}
И в моем приложении я звоню:
init(getActivity());
А потом startDiscovery();
начать сканирование и stopDiscovery();
остановить сканирование.
И, конечно же, я дал приложению необходимые разрешения в манифесте... Что мне здесь не хватает? Если вам нужно, чтобы я предоставил дополнительный код / информацию - просто спросите. Спасибо!!
1 ответ
Я являюсь автором браузера ZeroConf для Android и использую библиотеку JmDNS с открытым исходным кодом для всех своих решений. Это прекрасно работает, но есть несколько хитростей, чтобы заставить его работать должным образом.
В вашем Android manifest.xml убедитесь, что у вас есть хотя бы эти разрешения.
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
Перед началом действия вы должны разрешить многоадресные пакеты, установив многоадресную блокировку.
@Override protected void onStart() { Log.i(TAG, "Starting ServiceActivity..."); super.onStart(); try { Log.i(TAG, "Starting Mutlicast Lock..."); WifiManager wifi = (WifiManager) this.getSystemService(Context.WIFI_SERVICE); // get the device ip address final InetAddress deviceIpAddress = getDeviceIpAddress(wifi); multicastLock = wifi.createMulticastLock(getClass().getName()); multicastLock.setReferenceCounted(true); multicastLock.acquire(); Log.i(TAG, "Starting ZeroConf probe...."); jmdns = JmDNS.create(deviceIpAddress, HOSTNAME); jmdns.addServiceTypeListener(this); } catch (IOException ex) { Log.e(TAG, ex.getMessage(), ex); } Log.i(TAG, "Started ZeroConf probe...."); } private InetAddress getDeviceIpAddress(WifiManager wifi) { InetAddress result = null; try { // default to Android localhost result = InetAddress.getByName("10.0.0.2"); // figure out our wifi address, otherwise bail WifiInfo wifiinfo = wifi.getConnectionInfo(); int intaddr = wifiinfo.getIpAddress(); byte[] byteaddr = new byte[] { (byte) (intaddr & 0xff), (byte) (intaddr >> 8 & 0xff), (byte) (intaddr >> 16 & 0xff), (byte) (intaddr >> 24 & 0xff) }; result = InetAddress.getByAddress(byteaddr); } catch (UnknownHostException ex) { Log.w(TAG, String.format("getDeviceIpAddress Error: %s", ex.getMessage())); } return result; }
И не забудьте остановить сканирование, чтобы разблокировать многоадресную блокировку и отключить JmDNS.
@Override protected void onStop() { Log.i(TAG, "Stopping ServiceActivity..."); super.onStop(); stopScan(); } private static void stopScan() { try { if (jmdns != null) { Log.i(TAG, "Stopping ZeroConf probe...."); jmdns.unregisterAllServices(); jmdns.close(); jmdns = null; } if (multicastLock != null) { Log.i(TAG, "Releasing Mutlicast Lock..."); multicastLock.release(); multicastLock = null; } } catch (Exception ex) { Log.e(TAG, ex.getMessage(), ex); } }
Самое главное, не используйте конструктор по умолчанию. Вы должны использовать конструктор IP-адресов. Я заметил, что в вашем коде вы просто делаете JmDNS.create(). Я думаю, по какой-то причине единственный способ, которым он работает на Android, - это использовать приведенный ниже конструктор.
jmdns = JmDNS.create(deviceIpAddress, HOSTNAME);
Если у вас возникла эта ошибка в Android Oreo 8.x, это может вам помочь.
Во-первых, не забудьте убедиться, что вы добавили эти разрешения в свой Android manifest.xml
.
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
Получите блокировку многоадресной рассылки, чтобы разрешить многоадресные пакеты.
WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
MulticastLock lock = wifi.createMulticastLock("jmdns-multicast-lock");
lock.setReferenceCounted(true);
lock.acquire();
Теперь используйте только этот конструктор JmDNS.create(InetAddress addr, String name)
чтобы создать экземпляр JmDNS и привязать его к определенному сетевому интерфейсу с учетом его IP-адреса, например:
try {
jmDNS = JmDNS.create(InetAddress.getByName(obtainIPv4Address(info)), HOST_NAME);
} catch (IOException e) {
LogHelper.e(TAG, "Error in JmDNS creation: " + e);
}
Наконец, не забудьте позвонить JmDNSunreisterAllServices()
, а также JmDNS.close()
чтобы остановить поток JmDNS и освободить все связанные с ним системные ресурсы. Также звонитеMulticastLock.release()
чтобы разблокировать блокировку многоадресной рассылки, когда вы закончите с ней.
try {
if (jmDNS != null) {
jmDNS.unregisterAllServices();
jmDNS.close();
jmDNS = null;
}
if (lock != null) {
lock.release();
lock = null;
}
} catch (Exception e) {
e.printStackTrace();
}