Что значит привязать сокет многоадресной рассылки (UDP)?

Я использую многоадресную UDP между хостами, которые имеют несколько сетевых интерфейсов. Я использую boost:: asio, и меня смущают 2 операции, которые приемники должны сделать: bind, затем join-group.

почему вам нужно указать локальный адрес интерфейса во время привязки, когда вы делаете это с каждой многоадресной группой, к которой вы присоединяетесь?

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

Примечание: "join-group" - это оболочка над setsockopt(IP_ADD_MEMBERSHIP), который, как задокументировано, может вызываться несколько раз в одном сокете для подписки на разные группы (по разным сетям?). Поэтому имеет смысл отключить вызов bind и указать порт каждый раз, когда я подписываюсь на группу.

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

4 ответов


привязать UDP сокет при получении многоадресной рассылки означает указать адрес и порт, с которого будут приниматься данные (не локальный интерфейс, как в случае привязки акцептора TCP). Адрес, указанный в этом случае, имеет фильтрация роль, т. е. сокет будет получать только датаграммы, отправленные на этот многоадресный адрес и порт, независимо от того, какие группы впоследствии присоединяются к сокету. Это объясняет, почему при привязке к INADDR_ANY (0.0.0.0) я получил датаграммы, отправленные в мою многоадресную рассылку группа, тогда как при привязке к любому из локальных интерфейсов я ничего не получал, даже если датаграммы отправлялись по сети, которой этот интерфейс соответствовал.

цитата из UNIX® Network Programming Volume 1, Third Edition: The Sockets Networking API by W. R Stevens. 21.10. Отправка и получение

[...] Мы хотим, чтобы получающий сокет связывал многоадресную группу и порт, говорят 239.255.1.2 порт 8888. (Вспомните, что мы могли бы просто связать этот подстановочный IP-адрес и порт 8888, но привязка многоадресного адреса предотвращает получение сокетом любых других дейтаграмм, которые могут приехать, предназначенных для порт 8888.) Затем мы хотим, чтобы получающий сокет присоединиться к группе многоадресной рассылки. Отправляющий сокет будет отправлять датаграммы в этот же адрес многоадресной рассылки и порт, скажем, 239.255.1.2 порт 8888.


операция "bind "в основном говорит:" используйте этот локальный UDP-порт для отправки и получения данных. Другими словами, он выделяет этот порт UDP для исключительного использования для вашего приложения. (То же самое справедливо для сокетов TCP).

когда вы привязываетесь к "0.0.0.0" (INADDR_ANY), вы в основном говорите уровню TCP/IP использовать все доступные адаптеры для прослушивания и выбрать лучший адаптер для отправки. Это стандартная практика для большинства кодов сокетов. Единственный раз, когда ты не ... укажите 0 для IP-адреса, когда вы хотите отправить / получить на определенном сетевом адаптере.

аналогично, если вы укажете значение порта 0 во время привязки, ОС назначит случайно доступный номер порта для этого сокета. Поэтому я ожидал бы для многоадресной рассылки UDP, вы привязываетесь к INADDR_ANY на определенном номере порта, куда ожидается многоадресный трафик.

операция "join multicast group "(IP_ADD_MEMBERSHIP) необходима, потому что она в основном сообщает вашей сети адаптер для прослушивания не только кадров ethernet, где MAC-адрес назначения является вашим собственным, он также сообщает адаптеру ethernet (NIC) прослушивать IP-многоадресный трафик, а также для соответствующего многоадресного адреса ethernet. Каждый многоадресный IP-адрес сопоставляется с многоадресным ethernet-адресом. При использовании сокета send к определенному многоадресному IP-адресу MAC-адрес назначения на фрейме ethernet устанавливается в соответствующий многоадресный MAC-адрес для многоадресного IP-адреса. При присоединении к группе многоадресной рассылки, вы настраиваете NIC для прослушивания трафика, отправленного на тот же MAC-адрес (в дополнение к его собственному).

без аппаратной поддержки многоадресная рассылка не была бы более эффективной, чем простые широковещательные IP-сообщения. Операция соединения также сообщает маршрутизатору / шлюзу пересылать многоадресный трафик из других сетей. (Кто помнит, для mbone?)

Если вы присоединитесь к группе многоадресной рассылки, весь многоадресный трафик для всех портов на этом IP-адресе будет получен NIC. Только трафик, предназначенный для вашего привязанного порта прослушивания, будет передан стеку TCP/IP в ваше приложение. Что касается того, почему порты указаны во время многоадресной подписки - это потому, что многоадресный IP - это только-IP. "порты" являются свойством верхних протоколов (UDP и TCP).

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

IANA владеет MAC-адресом OUI 01: 00: 5e, поэтому многоадресная рассылка пакеты доставляются с использованием диапазона MAC-адресов Ethernet 01: 00: 5e:00:00:00 - 01:00:5e: 7f:ff: ff. Это 23 бита доступных адресное пространство. Первый октет (01) включает широковещательную / многоадресную передачу немного. Отображаются нижние 23 бита 28-разрядного многоадресного IP-адреса в 23 бита доступного адресного пространства Ethernet.


коррекция Что значит привязать сокет многоадресной рассылки (udp)? пока это частично верно по следующей цитате:

операция "bind "в основном говорит:" используйте этот локальный UDP-порт для отправки и получения данных. Другими словами, он выделяет этот порт UDP для исключительного использования для вашего приложения

существует один частный случай. Несколько приложений can использовать тот же порт для прослушивание (обычно это имеет практическое значение для многоадресных датаграмм) , если применяется опция SO_REUSEADDR:

int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); // create UDP socket somehow
...
int set_option_on = 1;
// it is important to do "reuse address" before bind, not after
int res = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*) &set_option_on, 
    sizeof(set_option_on));
res = bind(sock, src_addr, len);

если несколько процессов сделали такое "повторное связывание", то каждая датаграмма UDP, полученная на этом общем порту, будет доставлена каждому из процессов (обеспечивая естественное соединение с трафиком многоадресных передач).

a) попытка любой привязки ("exclusive "или" reuse") к свободному порту будет успешной

b) попытка "исключительной привязки" завершится неудачей, если порт уже "повторно связанный"

c) попытка "повторного использования привязки" завершится неудачей, если какой-то процесс сохранит "исключительную привязку"


также очень важно отличать отправляющий многоадресный сокет от принимающего многоадресного сокета.

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

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

  // This is a fix for that bug that causes Servers to pop offline/online.
  // Servers will intermittently pop offline/online for 10 seconds or so.
  // The bug only happens if the machine had a DHCP gateway, and the gateway is no longer accessible.
  // After several minutes, the route to the DHCP gateway may timeout, at which
  // point the pingponging stops.
  // You need 3 machines, Client machine, server A, and server B
  // Client has both ethernets connected, and both ethernets receiving CITP pings (machine A pinging to en0, machine B pinging to en1)
  // Now turn off the ping from machine B (en1), but leave the network connected.
  // You will notice that the machine transmitting on the interface with
  // the DHCP gateway will fail sendto() with errno 'No route to host'
  if ( theErr == 0 )
  {
     // inspired by 'ping -b' option in man page:      
     //      -b boundif
     //             Bind the socket to interface boundif for sending.
     struct sockaddr_in bindInterfaceAddr;
     bzero(&bindInterfaceAddr, sizeof(bindInterfaceAddr));
     bindInterfaceAddr.sin_len = sizeof(bindInterfaceAddr);
     bindInterfaceAddr.sin_family = AF_INET;
     bindInterfaceAddr.sin_addr.s_addr = htonl(interfaceipaddr);
     bindInterfaceAddr.sin_port = 0; // Allow the kernel to choose a random port number by passing in 0 for the port.
     theErr = bind(mSendSocketID, (struct sockaddr *)&bindInterfaceAddr, sizeof(bindInterfaceAddr));
     struct sockaddr_in serverAddress;
     int namelen = sizeof(serverAddress);  
     if (getsockname(mSendSocketID, (struct sockaddr *)&serverAddress, (socklen_t *)&namelen) < 0) {
        DLogErr(@"ERROR Publishing service... getsockname err");
     }
     else
     {
        DLog( @"socket %d bind, %@ port %d", mSendSocketID, [NSString stringFromIPAddress:htonl(serverAddress.sin_addr.s_addr)], htons(serverAddress.sin_port) );
     }

без этого исправления многоадресная отправка будет периодически получать sendto () errno "нет маршрута к хосту". Если кто-то может пролить свет на то, почему отключение DHCP-шлюза вызывает многоадресную отправку сокетов Mac OS X, чтобы запутаться, я хотел бы это услышать.