Android - jmdns не обнаруживает устройства

Я пытаюсь реализовать класс для обнаружения служб в сети. Я пытался работать с Android NSD, и он действительно обнаруживает службы, но он поддерживает только уровни 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 Browser для 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);
    

Ответ 2

Если у вас есть эта ошибка в 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();
}