java NIO присоединяется к многоадресному каналу в сетевом интерфейсе по умолчанию

Я использую java.nio.channels.DatagramChannel для отправки и получения многоадресных сообщений UDP. Коробка, на которой работает моя программа, может иметь несколько сетевых интерфейсов.

Я могу указать сетевой интерфейс вручную, используя опцию сокета для исходящих дейтаграмм:

NetworkInterface ni = NetworkInterface.getByName("eth0");
channel.setOption(StandardSocketOptions.IP_MULTICAST_IF, ni);

и передача сетевого интерфейса в метод соединения для входящих дейтаграмм:

MembershipKey key = channel.join(group, ni);

Но я бы хотел, чтобы мое приложение использовало интерфейс по умолчанию на основе таблиц маршрутизации. Для исходящих данных это просто. Я не должен указывать IP_MULTICAST_IF или передавать null в качестве сетевого интерфейса. Документ Java говорит следующее:

«Начальное / значение по умолчанию для этой опции сокета может быть нулевым, чтобы указать, что исходящий интерфейс будет выбран операционной системой, обычно на основе таблиц сетевой маршрутизации».

Но что насчет входящих данных. Метод «join» всегда требует указания сетевого интерфейса и не позволяет мне передавать значение null.

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

Есть ли способ это сделать?

Я использую Java 8 и ОС Linux.


person Sandro    schedule 13.01.2020    source источник


Ответы (2)


В API DatagramChannel нет явной поддержки для присоединения к группе многоадресной рассылки на интерфейсе, выбранном системой, но вы можете обойти это, используя MulticastSocket :: getNetworkInterface для получения заполнителя NetworkInterface. Итак, это должно делать то, что вы хотите:

NetworkInterface ni;
try (MulticastSocket s = new MulticastSocket()) {
    ni = s.getNetworkInterface();
}

MembershipKey key = channel.group(join, ni);

MulticastSocket создается исключительно для вызова getNetworkInterface.

person Alan Bateman    schedule 14.01.2020
comment
Отличный материал. Это может быть самое близкое. Несколько лет назад мне не удавалось заставить этот подход надежно работать на машинах с множеством интерфейсов, но, возможно, сейчас он работает лучше. Единственная проблема заключается в том, что для MulticastSocket не указана (пока) группа, поэтому дейтаграммы могут оказаться в непредусмотренной сети. - person Daniel; 14.01.2020
comment
Это решение выглядит странно. Но это работает !!! Большое тебе спасибо!!! - person Sandro; 14.01.2020
comment
@Daniel нам не нужно указывать группу для MulticastSocket, Multicast-сокет автоматически закрывается после завершения блока try, мы используем его только для получения экземпляра объекта NetworkInterface fo INADDR_ANY или 0.0.0.0 ... - person Sandro; 14.01.2020

TL; DR Я не думаю, что можно заставить его работать так, как вы хотите. По крайней мере, не на Java.

Особенность адресов класса D (многоадресная рассылка) заключается в том, что они действительны практически для любой совместимой сетевой карты. Нет согласованности между отправкой и получением, даже для одной и той же многоадресной группы. Я могу отправить на 229.111.222.333 на одном интерфейсе и присоединиться / прослушать ту же группу многоадресной рассылки на другом. Поскольку UDP не требует сеанса, такая настройка вполне подходит.

Даже если ОС могла выбрать правильный интерфейс, большинство из них не могут, и поэтому вам нужно указать его. Однако, если машина действует как многоадресный маршрутизатор (например, работает mrouted), это совсем другое дело, поскольку его возможности маршрутизации обычно контролируются /etc/mrouted.conf.

В качестве примера, если я сбрасываю таблицу маршрутизации для своего окна Windows с двумя сетевыми адаптерами (я знаю, что вы используете Linux, но обычно он не отображает записи таблицы маршрутизации для адресов класса D. И я считаю, что это подсказка ;-)) , Я получил это (нерелевантные записи опущены):

Network Destination        Netmask          Gateway       Interface  Metric
        224.0.0.0        240.0.0.0         On-link         127.0.0.1    131
        224.0.0.0        240.0.0.0         On-link       192.168.1.1    281
        224.0.0.0        240.0.0.0         On-link    172.89.123.123    281

Как видите, если я отправлю или послушаю, скажем, 224.123.123.123, ОС не будет иметь понятия, какой интерфейс выбрать, все они имеют одинаковое право. Он может использовать значение Metric, но в этом случае использование многоадресной рассылки в интерфейсе обратной связи не имеет смысла. Адреса класса D - это нечетные птицы, что дополнительно подтверждается "конечным адресом" 240.0.0.0 выше, помеченным как Netmask, хотя это явно не так.

«Начальное / значение по умолчанию для этой опции сокета может быть нулевым, чтобы указать, что исходящий интерфейс будет выбран операционной системой, обычно на основе таблиц сетевой маршрутизации».

Я не думаю, что это полностью точное утверждение. Или это может быть, если вы используете многосетевой блок и на одном из интерфейсов установлен флаг MULTICAST. Но если у вас их несколько, то возможно вы можете использовать ip add route для явного добавления определенного маршрута / NIC, но я сомневаюсь, что это сработает.

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

person Daniel    schedule 14.01.2020
comment
Не уверен в Windows и других ОС, но Linux позволяет нам передавать INADDR_ANY, когда вы присоединяетесь к группе многоадресной рассылки. Когда я реализовал эту функциональность на C, она отлично работает. В старом java.net.MulticastSocket есть метод join с одним аргументом, я не пробовал, но, похоже, он тоже должен работать правильно. Большинство ОС позволяют нам использовать несколько физических и логических сетевых интерфейсов и настраивать маршруты по умолчанию для разных адресов. - person Sandro; 14.01.2020