Конструкторы MulticastSocket и привязка к порту или SocketAddress

У меня может быть фундаментальное непонимание термина «привязка» здесь, но меня смущает использование MulticastSocket и его конструкторы. Они не делают того, что я понимаю, что они должны делать, должны делать, поэтому любой, кто может помочь мне прояснить мое недоразумение, будет признателен.

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

public class Main {
public static final int DEFAULT_MULTICAST_PORT = 5555;
public static final String multicastGroup = "225.4.5.6";
public static final String adapterName = "eth0";
public static final int MAX_PACKET_SIZE = 65507;

CharBuffer charBuffer = null;
Charset charset = Charset.defaultCharset();
CharsetDecoder decoder = charset.newDecoder();
static ByteBuffer message = ByteBuffer.allocateDirect(MAX_PACKET_SIZE);
static boolean loop = true;

static byte[] buffer = new byte[MAX_PACKET_SIZE];

public static void main(String[] args) {

    try {
        //MulticastSocket mSocket = new MulticastSocket(new InetSocketAddress("192.168.2.23", DEFAULT_MULTICAST_PORT));
        MulticastSocket mSocket = new MulticastSocket(DEFAULT_MULTICAST_PORT);
        mSocket.setReuseAddress(true);
        mSocket.setSoTimeout(5000);
        NetworkInterface nic = NetworkInterface.getByName(adapterName);
        mSocket.joinGroup(new InetSocketAddress(multicastGroup, DEFAULT_MULTICAST_PORT),NetworkInterface.getByName(adapterName));
        DatagramPacket p = new DatagramPacket(buffer, MAX_PACKET_SIZE);
        while (loop){
            try{
                mSocket.receive(p);
                System.out.println("Packet Received.");
            } catch (SocketTimeoutException ex){
                System.out.println("Socket Timed out");
            }
        }

    } catch (IOException ex){
        System.err.println(ex);
    }

}

}

К сожалению, как только я изменяю конструктор MulticastSocket на MulticastSocket(SocketAddress bindaddr), он перестает работать. Кажется, я могу использовать только конструктор привязки к порту, чтобы он работал, так что именно он привязывается при вызове этого конструктора, поскольку на данном этапе я не указал сетевой адаптер. (Я знаю, что позже присоединюсь к группе с определенным NetworkInterface, но как я могу быть уверен, что во время вызова конструктора он не привязывается к ЛЮБОМУ адаптеру?)

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

Может ли кто-нибудь объяснить, что на самом деле делает привязка к порту only и возможно ли прослушивание только на определенном NetworkInterface?

Обновлено №1 **

Прочитав ответы и обсудив это с коллегой по работе, я понял, что такое Java MulticastSocket:

  1. MulticastSocket () создает многоадресный сокет, привязанный к порту, выбранному случайным образом (базовой ОС хоста, привязанной к подстановочному адресу 0.0.0.0, то есть ВСЕМ сетевым картам. Однако вызов этого конструктора с null создает несвязанный MulticastSocket. В этом сценарии вызов метода bind (SocketAddress) привязывается к IP-адресу и порту.
  2. MulticastSocket (int port) создает сокет многоадресной рассылки, привязанный к определенному порту, но на КАЖДОМ IP-адресе.
  3. MulticastSocket (SocketAddress sa) создает сокет многоадресной рассылки, привязанный к указанному IP-адресу (который может быть ЛЮБЫМ IP-адресом, даже недопустимым адресом многоадресной рассылки) и портом.

Использование варианта 2 означает, что потенциально любой пакет, отправленный на указанный порт, независимо от его фактического назначения, будет передан в MulticastSocket. Я говорю потенциально, потому что многоадресные пакеты будут приходить только в том случае, если группа была присоединена (но другие пакеты, предназначенные для адресов, не относящихся к многоадресной рассылке, будут приходить при условии совпадения номера порта?)

Используя вариант 3, я могу привязаться к IP-адресу и только пакетам, чье место назначения совпадает, будут приходить в сокет. С этой опцией было бы вполне возможно выполнить привязку к IP-адресу определенного сетевого интерфейса, однако многоадресные пакеты не будут получены, потому что они не будут предназначены для определенного IP-адреса сетевой карты (вот почему я никогда не видел, чтобы они приходили на пример кода). Также можно выполнить привязку к допустимому адресу многоадресной рассылки, но в этом сценарии только определенные пакеты, место назначения которых совпадает с привязанным адресом многоадресной рассылки, будут приходить в сокет, независимо от вызовов joinGroup() .

Теперь вызовы joinGroup() не делают ничего для самого сокета, но выдают IGMP-запрос к базовой сетевой системе, чтобы гарантировать, что маршрутизаторы, сама ОС и т. Д. Действительно начинают маршрутизацию указанной многоадресной рассылки. пакетирует оборудование и сетевой стек и, наконец, сам Java MulticastSocket.

** Обновление 2 ** Цитата из "Сетевое программирование UNIX", Стивенс, Феннер, Рудофф:

Чтобы получить дейтаграмму многоадресной рассылки, процесс должен присоединиться к группе многоадресной рассылки, а также должен привязать сокет UDP к номеру prot, который будет использоваться в качестве номера порта назначения для дейтаграмм, отправленных в группу. Эти две операции различны и требуются обе. Присоединение к группе сообщает IP-уровню хоста и уровню канала данных о получении многоадресных дейтаграмм, отправленных в эту группу. Привязка порта - это то, как приложение указывает UDP, что оно хочет получать дейтаграммы, отправленные на этот порт. Некоторые приложения также связывают многоадресный адрес с сокетом в дополнение к порту. это предотвращает доставку в сокет любых других дейтаграмм, которые могут быть получены для этого порта на другие одноадресные, широковещательные или многоадресные адреса.

Думаю, это все объясняет.

** Обновление 3 ** Просто хотел опубликовать протестированный мной код, и в комментариях объясняется, что происходит с каждым из них.

/**
         * This first creates an UNBOUND Multicast Socket and then binds to
         * a port (but accepting the wildcard IP 0.0.0.0.
         * The Following WORKS:
         */

        /*MulticastSocket mSocket = new MulticastSocket(null);
        mSocket.bind(new InetSocketAddress(DEFAULT_MULTICAST_PORT));
        mSocket.setReuseAddress(true);
        mSocket.setSoTimeout(5000);
        NetworkInterface nic = NetworkInterface.getByName(adapterName);
        mSocket.joinGroup(InetAddress.getByName(multicastGroup));
        */

        /**
         * The following creates a a network socket and binds in the constructor
         * to a local adapter and port. Consequently it DOES not work because
         * it only allows destination ips that match the bound address & port
         * even though the desired group is joined.
         */

        /*MulticastSocket mSocket = new MulticastSocket(new InetSocketAddress("192.168.2.23", DEFAULT_MULTICAST_PORT));
        mSocket.setReuseAddress(true);
        mSocket.setSoTimeout(5000);
        NetworkInterface nic = NetworkInterface.getByName(adapterName);
        mSocket.joinGroup(InetAddress.getByName(multicastGroup));*/


        /**
         * The following binds to the same multicast group this is 'joined' later
         * and this works correctly. However if the join() is NOT called, no packets 
         * arrive at the socket, as expected.
         */

        /*MulticastSocket mSocket = new MulticastSocket(new InetSocketAddress(multicastGroup, DEFAULT_MULTICAST_PORT));

        mSocket.setSoTimeout(5000);
        NetworkInterface nic = NetworkInterface.getByName(adapterName);
        // Comment out the following line and it no longer workds correctly.
        mSocket.joinGroup(InetAddress.getByName(multicastGroup));*/

        /**
         * The following binds to a a specified port on 0.0.0.0 and joins 
         * a specific Multicast group on a specific adapter. This must mean that the IGMP must occur 
         * on the specified adapter.
         * 
         * ** This will ALSO receive packets addressed DIRECTLY to the ip 192.168.2.23 with the same
         * port as DEFAULT_MULTICAST_POR ***ONLY!!***
         */
        MulticastSocket mSocket = new MulticastSocket(DEFAULT_MULTICAST_PORT);
        mSocket.setReuseAddress(true);
        mSocket.setSoTimeout(5000);
        NetworkInterface nic = NetworkInterface.getByInetAddress(InetAddress.getByName("192.168.2.23"));
        mSocket.joinGroup(new InetSocketAddress(multicastGroup, DEFAULT_MULTICAST_PORT),NetworkInterface.getByName(adapterName));

        /**
         * The following binds to a specific address and port (i.e. adapter address)
         * and then ONLY accepts UDP packets with destination equal to that IP.
         */
        /*MulticastSocket mSocket = new MulticastSocket(new InetSocketAddress("192.168.2.23", DEFAULT_MULTICAST_PORT));
        mSocket.setReuseAddress(true);
        mSocket.setSoTimeout(5000);
        NetworkInterface nic = NetworkInterface.getByInetAddress(InetAddress.getByName("192.168.2.23"));*/

person D-Dᴙum    schedule 15.10.2013    source источник
comment
Создание многоадресного сокета с SocketAddress означает, что мы привязываем сокет к SocketAddress, верно? Итак, если мы присоединяемся к группе многоадресной рассылки, почему сокет не может получать дейтаграммы многоадресной рассылки ????   -  person Sivanandham    schedule 13.12.2018
comment
Вот предложение, которое не имеет абсолютно никакого смысла: _1 _... как оно может быть ТОЛЬКО и ТОЛЬКО одновременно. Это логически невозможно, поэтому я понятия не имею, о чем вы говорите, и хотел бы знать, потому что я также хотел бы узнать об этом больше.   -  person searchengine27    schedule 10.03.2020
comment
@ searchchengine27 возможно, если бы вы были более вежливы в своем ответе, я был бы рад помочь.   -  person D-Dᴙum    schedule 11.03.2020
comment
В этом нет никакого смысла. Вы буквально хотите драться из-за комментария, который буквально констатирует факты? В этом не было ничего вежливого или невежливого - это просто факт. Теперь вы невежливо :) Спасибо, что доказали, что не можете быть уважаемым членом сообщества :)   -  person searchengine27    schedule 11.03.2020


Ответы (2)


Если вы не укажете локальный IP-адрес при его создании или привязке, он будет привязан к 0.0.0.0, что означает «принимать ввод через любую сетевую карту». Обычно это то, что вы хотите.

Можно выполнить привязку к определенному IP-адресу, что неявно означает соответствующий сетевой адаптер, но некоторые системы, такие как Linux, похоже, ожидают, что многоадресные сокеты, если они связаны, будут привязаны к самой многоадресной группе. Для меня это не имеет никакого смысла: что, если вы захотите присоединиться к другой группе?

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

person user207421    schedule 15.10.2013
comment
После дальнейшего размышления я добавил дополнительную информацию к вопросу и был бы признателен за ваши мысли. - person D-Dᴙum; 16.10.2013
comment
Если вы используете свой вариант 3, вы также должны присоединиться через этот интерфейс, как я сказал выше. Тем не менее, у него все еще есть проблема с получением не-многоадресных рассылок. Вы можете попробовать привязать к самому многоадресному адресу, но он может не работать на вашей платформе, как я уже сказал выше. - person user207421; 17.10.2013
comment
EJP Я думаю, что мы в основном согласны с тем, что происходит, но я должен указать, что в сделанных мной tets можно привязать сокет Multicast к адресу NON Multicast, и это по существу предотвращает получение любого многоадресного пакета. Теперь я понимаю, что join() и bind () `- две разные вещи. Также возможно использовать этот MulticastSocket, связанный с адресом NON-Multicast, для получения дейтаграмм, предназначенных для этого адреса NON-Multicast. Это имеет смысл, поскольку MulticaseSocket - это просто специализация DatagramSocket. Я использую Linux, в котором разрешена привязка. Меня не волнует винда. - person D-Dᴙum; 17.10.2013
comment
Я скоро опубликую сводку своих тестов для справок в будущем, если кто-то еще глубоко задумается над этим ... - person D-Dᴙum; 17.10.2013
comment
он привязывается к 0.0.0.0, что означает «принимать ввод через любую сетевую карту» - это неверно - person haelix; 28.05.2014
comment
см. stackoverflow.com/questions/10692956/ - person haelix; 28.05.2014
comment
@haelix Ваш ответ согласуется со мной: это объясняет, почему при привязке к INADDR_ANY (0.0.0.0) я получил датаграммы, отправленные в мою группу многоадресной рассылки. - person user207421; 28.05.2014
comment
@Kerry Если вы привязываетесь к определенному адресу без многоадресной рассылки, который не является статическим IP-маршрутом, к которому исходят многоадресные рассылки, вы должны присоединиться к группе через сетевой адаптер, который является статическим маршрутом, или, для простоты, через все, как я сказал выше. Я нигде не вижу доказательств того, что вы это сделали. У меня есть рабочий продукт, и мой патч на этот счет был принят в Jini много лет назад. - person user207421; 28.05.2014
comment
@EJP при привязке к INADDR_ANY (0.0.0.0) Я получил датаграммы, отправленные в мою группу многоадресной рассылки - да, я это сделал, но не потому, что 0.0.0.0 означает прием ввода через любой nic. К сетевому адаптеру привязку пробовали? Не работает. Как объясняется в моем ответе и как я видел, что все работает (или нет), INADDR_ANY - это подстановочный знак для многоадресного адреса, а не для привязки NIC. - person haelix; 28.05.2014
comment
@haelix Вы ошибаетесь. 0.0.0.0 означает, что INADDR_ANY означает «любая сетевая карта», и ваш собственный опыт доказывает это. Он также используется при привязке прослушивающих сокетов в TCP, что не имеет ничего общего с многоадресной рассылкой. Предлагаю вам почитать. Моя книга все еще доступна в Springer. - person user207421; 29.05.2014
comment
Вы правы насчет TCP: адрес, указанный при привязке, является локальным интерфейсом или ЛЮБЫМ. Но для многоадресной рассылки, если я привязываюсь к сетевому интерфейсу, через который принимается трафик, я не получаю пакетов. Это подразумевает, что адрес привязки не адрес локального интерфейса, и, следовательно, INADDR_ANY не означает «любой сетевой адаптер». Это вывод, сделанный из моего опыта: адрес, указанный во время привязки, имеет роль фильтрации: привязка к INADDR_ANY означает отсутствие фильтрации, а привязка к фактической группе многоадресной рассылки означает фильтрацию пакетов, поступающих с других адресов. . - person haelix; 29.05.2014
comment
(Это согласуется с обновлением №2 в вопросе OP.) - person haelix; 29.05.2014
comment
@haelix Ваш опыт согласуется с моим ответом, и у вас нет никаких доказательств того, что ваш ответ более правильный. Предлагаю вам попробовать. Свяжите MulticastSocket, как вы описываете, а затем отправьте себе дейтаграмму на этот порт через реальный IP-адрес хоста. Если нет, то вы правы. Если да, то ошибаешься. Жду результата. - person user207421; 30.05.2014
comment
@EJP извините, мой опыт согласуется с моим ответом, а не вашим. Я уже проводил эксперименты и на этом основываю свои ответы. Мне неинтересно повторять, поскольку я нашел в литературе свидетельства того, что моя точка зрения верна - API сети сокетов от W.R Stevens - как указано в моих связанных вопросах и ответах выше. - person haelix; 30.05.2014
comment
@haelix Мы все еще ждем ваших доказательств. Вы утверждаете условие, которое не фигурирует в документации, которое вы не проверили экспериментально, и ожидаете, что другие люди его примут? - person user207421; 12.01.2020

Я думаю, что вам не хватает точки привязки вашего адреса:

http://download.java.net/jdk7/archive/b123/docs/api/java/net/MulticastSocket.html
Группа многоадресной рассылки определяется IP-адресом класса D и стандартным номером порта UDP. IP-адреса класса D находятся в диапазоне от 224.0.0.0 до 239.255.255.255 включительно. Адрес 224.0.0.0 зарезервирован и не должен использоваться.

person Germann Arlington    schedule 15.10.2013
comment
Точно упустили суть? На первый взгляд, его многоадресный адрес мне кажется нормальным. - person user207421; 16.10.2013
comment
@EJP Я говорю о закомментированном коде, который НЕ работает, как я понял //MulticastSocket mSocket = new MulticastSocket(new InetSocketAddress("192.168.2.23", DEFAULT_MULTICAST_PORT)); - person Germann Arlington; 16.10.2013
comment
Германн, спасибо за ответ. Не могли бы вы прокомментировать добавленную мной дополнительную информацию? - person D-Dᴙum; 16.10.2013
comment
Я не проверял, действительно ли трафик идет на ВСЕ адреса - может быть интересно, к сожалению, у меня сейчас недостаточно компьютеров, чтобы проверить себя. Если вы можете настроить его и сообщить о своих выводах. - person Germann Arlington; 16.10.2013
comment
AFAIR Multicast в основном аналогичен широковещательной рассылке (т.е. пакеты будут отправляться на ВСЕ доступные адреса), разница в том, что вы должны подписаться (присоединиться) для получения многоадресной рассылки. Где и как вы обнаружили, что joinGroup () на самом деле влияет на сеть, а не на получателя? Я ожидаю, что вариант 3 - привязка MulticastSocket к недопустимому адресу завершится ошибкой либо сразу, либо, по крайней мере, как только вы попытаетесь начать его использовать. - person Germann Arlington; 16.10.2013
comment
Germann, я пробовал использовать MulticastSocket способами, которые я подробно описал в дополнительной информации выше (помимо проверки, проходят ли «нормальные» IP-адреса, если они привязаны к «нормальному» IP), и до сих пор кажется, что это работает так, как я описал выше. . Также цитируется из книги Unix Network Programming, Stevens, Fenner, Rudoff, Когда процесс на узле присоединяется к группе многоадресной рассылки, этот узел отправляет сообщение IGMP на любые подключенные маршрутизаторы многоадресной рассылки ... Многоадресная рассылка - это больше, чем просто фильтрация на уровне приложения. для взаимодействия также требуется оборудование вне хоста. - person D-Dᴙum; 17.10.2013
comment
Я повторяю. Что он упустил в закомментированном коде? Вы не ответили на это. И ваши замечания о мультикастинге совершенно некорректны. Керри явно знает об этом гораздо больше, чем вы. Запрос на присоединение задействуется в протоколе IGMP, чтобы сообщить ближайшему маршрутизатору, что на этой стороне есть участник, поэтому он знает, что нужно пересылать многоадресные рассылки в эту группу: в противном случае это не так. Вот почему многоадресная рассылка отличается от широковещательной передачи. Кажется, вы просто догадываетесь. - person user207421; 17.10.2013
comment
Германн, я не осознавал, что binding и joining - две совершенно разные вещи. Вы можете привязаться к ЛЮБОМУ IP-адресу, но вы можете присоединиться только к многоадресному IP-адресу. - person D-Dᴙum; 17.10.2013