Обнаружение всех доступных сетевых широковещательных адресов в Java

В моем проекте я хотел получить список всех доступных широковещательных адресов, чтобы я мог транслировать запрос, а другое приложение, расположенное на другом компьютере в неуказанной сети, отвечало бы и получало список я (теперь использую небольшую измененную версию с вклад Майка) пришли к следующему:


private ArrayList<InetAddress> getBroadcastAddresses() {
        ArrayList<InetAddress> listOfBroadcasts = new ArrayList();
        Enumeration list;
        try {
            list = NetworkInterface.getNetworkInterfaces();

            while(list.hasMoreElements()) {
                NetworkInterface iface = (NetworkInterface) list.nextElement();

                if(iface == null) continue;

                if(!iface.isLoopback() && iface.isUp()) {
                    System.out.println("Found non-loopback, up interface:" + iface);

                    Iterator it = iface.getInterfaceAddresses().iterator();
                    while (it.hasNext()) {
                        InterfaceAddress address = (InterfaceAddress) it.next();

                        System.out.println("Found address: " + address);

                        if(address == null) continue;
                        InetAddress broadcast = address.getBroadcast();
                        if(broadcast != null) listOfBroadcasts.add(broadcast);
                    }
                }
            }
        } catch (SocketException ex) {
            return new ArrayList<InetAddress>();
        }

        return site;
}

Это хорошо работает для reqular LAN, но когда дело доходит до Wi-Fi LAN, он просто пропускает второй цикл while после одного шага из-за того, что address равно нулю, хотя при использовании System.out.println(interfaceItem) просто для просмотра того, какие интерфейсы через него написано имя беспроводной ЛВС и мой IP-адрес, соответствующий сети.

ИЗМЕНИТЬ 1: Это - это вывод, где 172.16.1.104 - это мой IP-адрес в беспроводной сети. Проблема появляется ТОЛЬКО на моем ноутбуке с Wi-Fi. Выход из моего ноутбука, где я в основном использую беспроводную связь, а иногда я использую UTP для подключения к моему другу. Существует также один сетевой интерфейс VirtualBox на моем ноутбуке.

Не могли бы вы рассказать мне, что с ним не так? Спасибо!

Примечание. Таким образом, оказывается, что это может быть проблемой для моего ноутбука, в частности, и код работает для всех остальных в целом, мне нравится такая проблема:-) Кажется, это тупик для меня, но спасибо за помощь в любом случае:-)

Еще люблю тебя!; -)

Ответы

Ответ 1

Я думаю, вам нужно будет перебирать все адреса и дополнительно проверить, есть ли широковещательный адрес null.

Учтите, что у вас могут быть адреса, которые вы также не ожидаете от назначенного интерфейса. В моей системе Linux, с вашим кодом, первый адрес, который я вижу, является адресом IPv6, с нулевой трансляцией (поскольку нет такой вещи, как широковещательная передача IPv6, хотя вы можете использовать многоадресную передачу для достижения того же эффекта).

Вам нужно полностью удалить раздел 1st way кода. Когда вы continue;, вы перейдете к следующему интерфейсу, а не рассмотрите возможность наличия двух адресов.

Другая причина, по которой вы всегда хотите перебирать все адреса, которые могут иметь широковещательные передачи, заключается в том, что вам необходимо учитывать, что у вас могут быть адреса в двух сетях, назначенных интерфейсу. Например, у вас может быть интерфейс с назначенными 192.168.0.1/24 и 172.16.0.1/24.

Также рассмотрите возможность использования Set для хранения широковещательных адресов для защиты от случая, когда у вас могут быть два адреса в одной и той же подсети.

Наконец, поскольку использование широковещательных адресов ограничит вас разговорами только с хостами, у которых есть IP-адрес в той же подсети, вы можете пропустить узлы, которые не настроены должным образом с той же подсети/сетевой маской. Поэтому вы можете рассмотреть возможность использования многоадресной рассылки для этого; вы можете использовать IPv4 (или IPv6) все узлы многоадресной рассылки для доступа ко всем узлам в подсети, независимо от настроенного адреса. (224.0.0.1 и FF01:: 1, соответственно)

Изменить: У вас также есть ошибка в 2nd way, связанная с использованием итератора. Поскольку вы получаете новый .iterator() каждый раз за цикл for, вам повезло, здесь нет бесконечного цикла. Я изменил код на это, и он работает для меня:

$ cat Broadcasts.java 
import java.net.*;
import java.util.*;

public class Broadcasts
{
    public static void main(String[] args)
    {
        HashSet<InetAddress> listOfBroadcasts = new HashSet<InetAddress>();
        Enumeration list;
        try {
            list = NetworkInterface.getNetworkInterfaces();

            while(list.hasMoreElements()) {
                NetworkInterface iface = (NetworkInterface) list.nextElement();

                if(iface == null) continue;

                if(!iface.isLoopback() && iface.isUp()) {
                    //System.out.println("Found non-loopback, up interface:" + iface);

                    Iterator it = iface.getInterfaceAddresses().iterator();
                    while (it.hasNext()) {
                        InterfaceAddress address = (InterfaceAddress) it.next();
                        //System.out.println("Found address: " + address);
                        if(address == null) continue;
                        InetAddress broadcast = address.getBroadcast();
                        if(broadcast != null) 
                        {
                            System.out.println("Found broadcast: " + broadcast);
                            listOfBroadcasts.add(broadcast);
                        }
                    }
                }
            }
        } catch (SocketException ex) {
            System.err.println("Error while getting network interfaces");
            ex.printStackTrace();
        }

        // return listOfBroadcasts;
    }
}

Еще одна проблема, с которой вы можете столкнуться, - это try/catch вокруг основной функции, которая заставит этот код остановиться, если он ударит что-то неожиданное. Было бы лучше окружить возможные точки отказа с помощью try/catch и сделать что-то здравомыслящее (например, пропустить интерфейс или адрес), но я не смотрел, какие методы могут генерировать исключения.

Изменить 2: я неправильно читаю ваш код; ваш итератор был в порядке.;-) Проблема (я указал ранее) заключалась в том, что ваш 1st way замыкает ваш 2nd way; так как он попадает в оператор continue;, если первый адрес null, вы даже не пытаетесь пропустить их все.

В любом случае, запустите с этими операторами println и опубликуйте результаты, если у вас все еще есть проблемы.

Изменить 3: Хорошо, я сдаюсь.;-) Исходя из выведенного вами вывода, похоже, что вы используете ошибку в классе NetworkInterface.

Я не знаю, поможет ли это отключить параметр preferIPv4Stack, но вы должны это проверить. Я немного искал сообщения об ошибках, которые описывают это поведение и не могут найти.

Поскольку вы работаете в Linux, вы всегда можете воспользоваться альтернативным методом обхода и вызвать что-то вроде:

/sbin/ip addr | perl -ne 'print "$1\n" if $_  =~ /inet.* brd ([0-9\.]*)/'

... который должен вернуть вам список широковещательных адресов.

Изменить 4. Я только что заметил в JavaDoc для NetworkInterface есть вызов getSubInterfaces(). Возможно, вам нужно позвонить этому, чтобы убедиться, что вы получили все адреса? (это может помочь разместить вывод /sbin/ip addr и /sbin/ifconfig)

Изменить 5. Что касается только что добавленной награды. (Этот вопрос старше года!) Может кто-нибудь, пожалуйста, запустите код в моем ответе выше (отредактировано, чтобы было легко скопировать/вставить/запустить) и сказать, работает ли он? Если это не так, отредактируйте этот вопрос и обратите внимание на точные ошибки/проблемы.