Не удается захватить широковещательные пакеты UDP с помощью Java DatagramSocket

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

Раньше мой код работал на машинах с Linux и Windows. Я бы транслировал простое сообщение, и его можно было бы прочитать. Я также мог видеть это сообщение при просмотре сетевого трафика с помощью Wireshark.

Подход, который я использую, заключается в том, чтобы

  1. Получите широковещательный адрес(а) в сети на сервере.
  2. В течение заданного времени транслируйте сообщение (скоро будет IP-адрес сервера)
  3. На стороне клиента дождитесь получения сообщения.

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

Код трансляции сервера:

public class Broadcaster {
    /* ... */
    public void pulse() throws InterruptedException, IOException, SocketException {
        Long elapsed = new Date().getTime();
        Long timeout = elapsed + this.duration;
        DatagramPacket packet = new DatagramPacket(this.message.getBytes(), this.message.length());
        HashSet<InetAddress> channels = Broadcaster.getBroadcastChannels();

        while(elapsed <= timeout) {
            for(InetAddress channel : channels) {
                DatagramSocket socket = new DatagramSocket(this.port);      
                socket.setBroadcast(true);
                socket.connect(channel, this.port);
                socket.send(packet);        
                System.out.println("Broadcast sent to " + channel.getHostAddress() + " (" + socket.getPort() + "): " + this.message);       
                socket.close();
            }   
            Thread.sleep(this.frequency);
            elapsed = new Date().getTime();
        }
    }

    private static HashSet<InetAddress> getBroadcastChannels() throws SocketException {
        /* Returns 192.168.0.255 */
    }

    public static void main(String[] args) {
        Broadcaster heart = new Broadcaster("Hello from the Raspberry Pi!", 120000, 5000, 8027);
        try {
            heart.pulse();
        } catch(SocketException e) {
            /* ...etc... */
        } finally {
            System.out.println("Broadcasting completed.");
        }
    }
}

Код клиента:

public class BroadcastListener {
    private int port;
    private int length;

    public BroadcastListener(int length, int port) {
        this.port = port;
        this.length = length;
    }

    public String getNext() throws IOException {
        byte buffer[] = new byte[this.length];  
        DatagramSocket socket = new DatagramSocket(this.port);
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);  
        System.out.println("Waiting on " + socket.getLocalSocketAddress()); 
        socket.receive(packet);
        socket.close();

        return new String(buffer);
    }

    public static void main(String[] args) {
        System.out.println("Listening for network broadcasts...");
        BroadcastListener broadcast = new BroadcastListener(128, 8027);

        try {
            System.out.println("Received broadcast: " + broadcast.getNext());
        } catch(IOException e) {
            System.out.println("Could not receive broadcasts:");
            System.out.println(e.getMessage());
        }
    }
}

Адрес широковещательной рассылки/маски сети, отображаемый на обоих устройствах ifconfig, равен netmask 255.255.255.0 broadcast 192.168.0.255.

Что меня больше всего смущает, так это то, что Wireshark все еще видит трансляцию, но когда я запускаю клиентскую программу Java, она просто сидит на socket.receive(packet);

скриншот Wireshark на Imgur

И клиент, и сервер подключены к порту 8027. Понятно, что вещатель работает, но прослушиватель клиентского вещания — нет. Кто-нибудь знает, что может происходить? Спасибо!


person jamsesso    schedule 30.12.2013    source источник


Ответы (3)


Как упоминалось в комментариях: проверьте свой брандмауэр :-)

Еще одна вещь, которую я понял, заключалась в том, что если я обнюхиваю с помощью wireshark, никакие другие процессы не смогут получить эти дейтаграммы. Поняв это, я написал скрипт nodejs, который по умолчанию эксклюзив=ложь, но даже это не помогло. Возможно, есть флаг ядра или что-то еще, что дейтаграммы UDP не могут быть «потреблены» одним процессом.

person sja    schedule 15.05.2015

Похоже, ваш сервер может вещать по адресу, отличному от адреса вашего клиента. Вашему клиенту не назначен InetAddress, попробуйте использовать этот конструктор для вашего сокета в BroadcastListener

DatagramSocket socket = new DatagramSocket(new InetSocketAddress("192.168.0.255", this.port));

Если это не сработает, вы можете попробовать привязать сервер и клиент к 127.0.0.1.

person ug_    schedule 30.12.2013
comment
Широковещательный адрес для клиента и сервера одинаков, что подтверждается выводом утилиты ifconfig. Единственная разница, которую я вижу в обоих выводах, заключается в том, что сервер находится на интерфейсе eth0, а клиент — на интерфейсе wlp3s0. Хотя я не думаю, что это имеет значение. - person jamsesso; 31.12.2013
comment
@SamJesso Я скопировал и вставил ваш код и запустил его с помощью локального хоста. Работал нормально для меня. - person ug_; 31.12.2013
comment
После дальнейшего расследования кажется, что обновление до Fedora 20 снова включило защиту брандмауэра моей системы от широковещательных передач. Мальчик, я когда-нибудь чувствую себя тусклым. Я знал, что это должно быть просто... Спасибо за помощь! - person jamsesso; 31.12.2013
comment
Опуская локальный адрес, он автоматически получает через все локальные интерфейсы, т.е. 0.0.0.0 или INADDR_ANY. - person user207421; 15.05.2015

Вы продолжаете создавать и разрушать DatagramSockets. Если пакет прибывает на ваш хост в момент, когда у вас нет привязки DatagramSocket к порту, он будет отброшен.

Создайте один DatagramSocket и оставьте его открытым на всю жизнь этого кода.

person user207421    schedule 15.05.2015