Отправлять и получать многоадресные пакеты UDP с одним и тем же сокетом

Я не смог найти рабочий пример для этого, поэтому я собираюсь опубликовать вопрос (и давайте посмотрим, смогу ли я сократить его до примера кода MVP). Итак, мне нужно сделать запросы mdns, я могу использовать два сокета (один для отправки / второй для получения), но пока мне не удалось заставить его работать с одним сокетом.

Итак, шаги, которые я выполняю:

Перечислите все адреса интерфейсов на хосте. Затем для каждого хоста:

  1. Создайте неблокирующий сокет UDP
  2. bind() либо на адрес интерфейса: 5353, либо на адрес группы многоадресной рассылки (любой из них приводит к сбою чтения или записи)
  3. Установите IP_MULTICAST_IF для локального интерфейса
  4. IP_ADD_MEMBERSHIP в группу многоадресной рассылки
  5. Добавьте сокеты для чтения/записи в kqueue

Итак, в зависимости от того, что я делаю на шаге 2, чтение или запись завершаются ошибкой:

  1. Если я привязываю сокет к группе многоадресной рассылки, я получаю уведомления о прочтении из kqueue и могу читать пакеты, но когда я получаю уведомление о записи из kqueue и пытаюсь записать, происходит сбой с ошибкой 49.
  2. Если я привязываю сокет к адресу интерфейса, я могу отправлять пакеты, но из kqueue никогда не приходят уведомления о прочтении.

Итак, к какому адресу мне нужно привязаться? Кроме того, поскольку параметр IP_ADD_MEMBERSHIP ip_mreq имеет поле интерфейса, действительно ли мне нужен IP_MULTICAST_IF?


person Rudolfs Bundulis    schedule 26.03.2020    source источник


Ответы (1)


Если вы привязываетесь к определенному адресу интерфейса (по крайней мере, в Linux), вы не сможете получать многоадресную рассылку. Если вы привязываетесь к многоадресному адресу, вы не можете отправлять.

Что вам нужно сделать, так это привязаться к INADDR_ANY, затем, когда вы устанавливаете опцию IP_ADD_MEMBERSHIP, вы устанавливаете адрес интерфейса, на который вы хотите получать многоадресные пакеты. Если у вас есть несколько интерфейсов, вы можете вызвать это для каждого интерфейса, к которому хотите привязаться.

person dbush    schedule 26.03.2020
comment
Попробую это прямо сейчас. Только одно уточнение: когда вы говорите, что у вас есть несколько интерфейсов, вы можете вызывать это для каждого интерфейса, который вы хотите связать с вами, по-прежнему означая отдельные сокеты, или я действительно могу сделать это с одним сокетом и несколькими вызовами IP_ADD_MEMBERSHIP? - person Rudolfs Bundulis; 26.03.2020
comment
@RudolfsBundulis Один сокет, несколько вызовов IP_ADD_MEMBERSHIP, по одному разу для каждого интерфейса. - person dbush; 26.03.2020
comment
спасибо, сделал как вы посоветовали помогло. Хорошо, и последнее: одной из идей использования нескольких сокетов была возможность узнать, что сетевой интерфейс был отключен (в этом случае я ожидал получить событие ошибки epoll/kqueue). Не уверен, что это произойдет, но если да, то как отключение интерфейса повлияет на сокет, который присоединился к нескольким группам? - person Rudolfs Bundulis; 26.03.2020
comment
Хорошо, только что проверил, что включение/выключение интерфейсов не вызывает ошибки, поэтому мне придется обрабатывать это отдельно. - person Rudolfs Bundulis; 27.03.2020