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

Я пытаюсь реализовать класс для обнаружения сервисов в сети. Я пытался работать с Android NSD, и он обнаружил сервисы в порядке, но он поддерживает только уровни API 16 и выше, и я не могу получить поле txtRecord в информации о сервисе (по какой-то причине оно возвращает null). Оказывается, это известная проблема...

Итак, теперь я пытаюсь работать с 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();, чтобы остановить сканирование.

И, конечно же, я дал приложению необходимые разрешения в манифесте... Что мне здесь не хватает? Если вам нужно, чтобы я предоставил дополнительный код/информацию - просто спросите. Благодарность!!


person orenk86    schedule 22.05.2014    source источник


Ответы (2)


Я являюсь автором ZeroConf Browser для Android и использую библиотеку JmDNS с открытым исходным кодом для всех своих разрешений. Он отлично работает, но есть несколько хитростей, чтобы заставить его работать правильно.

  1. В вашем 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" />
    
  2. Перед началом действия вы должны разрешить многоадресную передачу пакетов, установив блокировку многоадресной рассылки.

    @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;
    }
    
  3. И не забудьте при остановке сканирования разблокировать многоадресную блокировку и отключить 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);
        }
    }
    
  4. Самое главное не используйте конструктор по умолчанию. Вы должны использовать Конструктор IP-адресов. Я заметил, что в вашем коде вы просто выполняете JmDNS.create(). Я думаю, что по какой-то причине единственный способ, которым это работает на Android, — это использовать конструктор ниже.

    jmdns = JmDNS.create(deviceIpAddress, HOSTNAME);
    
person Melloware    schedule 25.05.2014
comment
Работает! Оказывается, я использовал неправильный конструктор, поэтому проблему решил пункт 4. Бесконечно благодарен!! :) - person orenk86; 25.05.2014
comment
Одна вещь, которая меня убивает, - это то, как можно быстро найти устройства в локальной сети с несколькими доступными типами услуг. Ваш браузер быстрый! Можете ли вы подсказать, как вы это делаете? Я работаю над удаленным управлением для устройств IoT, и мне нужно быстро узнать все, что находится в локальной сети (и получить информацию об устройстве в процессе). - person German; 07.11.2014
comment
Значит, вы говорите, что ваш код недостаточно быстро находит сервисы в моем приложении ZeroConf Browser? Все, что я использую, это приведенный выше код, поэтому теоретически ваш должен быть таким же быстрым, как мой? - person Melloware; 08.11.2014
comment
Наоборот, я говорю, что у вас намного быстрее, чем у меня!! Потому что я перебираю огромный список сервисов (медленно). Так что, пожалуйста, в качестве общего совета, как вы находите так много услуг за короткое время? У вас есть список услуг для поиска? jmDNS уже находит все сервисы? Все примеры, которые я вижу, показывают, как подключиться к конкретному сервису. Но я хочу видеть меню доступных сервисов в локальной сети :) - person German; 12.11.2014
comment
Не берите в голову. Я неправильно использовал jmdns. Я брал сервисы из своего большого списка и не подписывался на изменения типа сервиса (теперь все это имеет смысл). Спасибо в любом случае! - person German; 14.11.2014
comment
@Melloware: мне нужно получить информацию обо всех беспроводных устройствах (не только мобильных устройствах Android), которые подключены к моей сети Wi-Fi. например, IP-адреса, MAC-адреса, имя поставщика, тип устройства (например, ноутбук, мобильный телефон, кондиционер, холодильник) и имена хостов. Могу ли я использовать jmDNS/ZerConf для своих нужд? - person Santhosh; 19.04.2016
comment
@SANTHOSH Нет, ты не можешь. Это только для обнаружения служб, предоставляемых Bonjour. Не обычный сетевой сканер, который, я думаю, вы ищете. - person Melloware; 21.04.2016
comment
@Melloware: что отвечает моему требованию? Не могли бы вы предложить мне? stackoverflow.com/questions/36567725/ - person Santhosh; 22.04.2016
comment
Где getDeviceIpAddress()? - person emc; 24.11.2017
comment
@emc Я только что отредактировал сообщение, добавив метод getDeviceIpAddress(). - person Melloware; 25.11.2017
comment
@Melloware Поздно на вечеринку, но что плохого в вызове InetAddress.getLocalHost() для определения локального IP-адреса? Не гарантируется, что будет интерфейс Wi-Fi? - person JoeHz; 27.03.2019
comment
@JoeHz, вы можете попробовать это, поскольку вы можете видеть, что этому сообщению уже 5 лет, но единственный надежный способ, которым я мог получить правильное разрешение IP для всех случаев, - это использовать описанный выше трюк. Я помню, что в старых версиях Android были некоторые причуды с getLocalHost(). Хотя попробовать точно можно! - person Melloware; 27.03.2019
comment
@melloware, разве этот отличный учебник не должен стать таким; учебник по использованию jmdns для Android? Просто потратил день на размышления, как работает код на Windows, но не на Android. Я уверен, что после этого. Спасибо! - person carl; 10.05.2019
comment
@carl да, мне просто нужно время, чтобы написать где-нибудь в блоге. Обычно я просто указываю людям здесь. :0 - person Melloware; 10.05.2019
comment
@меллоуэр. Как предполагалось, это сработало. Пожалуйста, поместите одну строку со ссылкой в ​​файле readme GitHub, указывающую здесь для (будущих) пользователей Android. - person carl; 25.05.2019
comment
@carl, ты имеешь в виду в JMDNS Github? Я не коммиттер, поэтому все, что я могу сделать, это предложить это. - person Melloware; 26.05.2019
comment
@melloware, хорошо. Думал, что вы были, но предложение - хорошая альтернатива, да - person carl; 04.06.2019

Если у вас возникает эта ошибка в 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();
}
person Teocci    schedule 27.05.2019
comment
getIPv4Address() должен быть вашим локальным методом @Teocci, но заменив его и следуя вашей схеме чайной ложкой (она уже была), он работает не на Android 8, а на Android 7. Я думаю, у нас есть проблема . Может ли быть что-то в настройках безопасности Android 8? Я использую LG V30. - person carl; 19.09.2019