sockets udp bind boost-asio multicast

sockets - ¿Qué significa vincular un socket de multidifusión(UDP)?



bind boost-asio (4)

Estoy usando UDP de multidifusión entre hosts que tienen múltiples interfaces de red. Estoy usando boost :: asio, y estoy confundido por los 2 receptores de operaciones que tienen que hacer: bind, luego join-group.

¿Por qué necesita especificar la dirección local de una interfaz, durante el enlace, cuando lo hace con cada grupo de multidifusión al que se une?

La pregunta hermana se refiere al puerto de multidifusión: ya que durante el envío se envía a una dirección y puerto de multidifusión, por eso, durante la suscripción a un grupo de multidifusión, solo se especifica la dirección, no el puerto; el puerto se especifica en la confusa llamada a enlazar.

Nota: el "join-group" es un wrapper sobre setsockopt(IP_ADD_MEMBERSHIP) , que según lo documentado, puede ser llamado varias veces en el mismo socket para suscribirse a diferentes grupos (¿sobre diferentes redes?). Por lo tanto, tendría perfecto sentido abandonar la llamada de enlace y especificar el puerto cada vez que me suscribo a un grupo.

Por lo que veo, siempre vinculando a "0.0.0.0" y especificando la dirección de la interfaz cuando se une al grupo, funciona muy bien. Confuso.


Corrección de ¿Qué significa vincular un socket de multidifusión (udp)? siempre y cuando sea parcialmente cierto en la siguiente cita:

La operación "vincular" básicamente dice: "use este puerto UDP local para enviar y recibir datos. En otras palabras, asigna ese puerto UDP para uso exclusivo para su aplicación

Hay un caso especial. Múltiples aplicaciones pueden compartir el mismo puerto para escuchar (generalmente tiene un valor práctico para datagramas de multidifusión) si se aplica la opción 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);

Si varios procesos hicieron tal "enlace de reutilización", entonces cada datagrama UDP recibido en ese puerto compartido se entregará a cada uno de los procesos (proporcionando un tráfico natural de multidifusión).

a) intento de cualquier enlace ("exclusivo" o "reutilización") a puerto libre será exitoso

b) el intento de "vinculación exclusiva" fallará si el puerto ya está "reutilizado".

c) se intentará "reutilizar el enlace" si algún proceso mantiene la "vinculación exclusiva"


La operación "vincular" básicamente dice: "use este puerto UDP local para enviar y recibir datos. En otras palabras, asigna ese puerto UDP para uso exclusivo para su aplicación. (Lo mismo ocurre con los sockets TCP).

Cuando se vincula a "0.0.0.0" (INADDR_ANY), básicamente está diciendo a la capa TCP / IP que use todos los adaptadores disponibles para escuchar y elija el mejor adaptador para enviar. Esta es una práctica estándar para la mayoría de los códigos de socket. La única vez que no especifique 0 para la dirección IP es cuando desea enviar / recibir en un adaptador de red específico.

De manera similar, si especifica un valor de puerto de 0 durante el enlace, el sistema operativo asignará un número de puerto disponible al azar para ese socket. Por lo tanto, esperaría que para la multidifusión UDP, se vincule a INADDR_ANY en un número de puerto específico donde se espera que se envíe el tráfico de multidifusión.

La operación "unirse al grupo de multidifusión" (IP_ADD_MEMBERSHIP) es necesaria porque básicamente le dice a su adaptador de red que no solo escuche las tramas de Ethernet donde la dirección MAC de destino es suya, sino que también le dice al adaptador de ethernet (NIC) que escuche el tráfico de multidifusión IP también para la dirección de Ethernet de multidifusión correspondiente. Cada IP de multidifusión se asigna a una dirección de Ethernet de multidifusión. Cuando utiliza un envío de socket a una IP de multidifusión específica, la dirección MAC de destino en la trama de Ethernet se establece en la dirección MAC de multidifusión correspondiente para la IP de multidifusión. Cuando se une a un grupo de multidifusión, está configurando la NIC para escuchar el tráfico enviado a esa misma dirección MAC (además de la suya).

Sin el soporte de hardware, la multidifusión no sería más eficiente que los mensajes IP de difusión simple. La operación de unión también le dice a su enrutador / puerta de enlace que reenvíe el tráfico de multidifusión desde otras redes. (¿Alguien recuerda MBONE?)

Si se une a un grupo de multidifusión, la NIC recibirá todo el tráfico de multidifusión para todos los puertos en esa dirección IP. Solo el tráfico destinado a su puerto de escucha vinculado pasará a la pila de TCP / IP. En cuanto a por qué se especifican los puertos durante una suscripción de multidifusión, es porque IP de multidifusión es solo eso, solo IP. "puertos" son una propiedad de los protocolos superiores (UDP y TCP).

Puede leer más sobre cómo las direcciones IP multicast se correlacionan con las direcciones Ethernet de multidifusión en varios sitios. El artículo de Wikipedia es tan bueno como se puede:

La IANA posee la dirección MAC de OUI 01: 00: 5e, por lo tanto, los paquetes de multidifusión se entregan utilizando el rango de direcciones MAC de Ethernet 01: 00: 5e: 00: 00: 00 - 01: 00: 5e: 7f: ff: ff. Esto es 23 bits de espacio de direcciones disponible. El primer octeto (01) incluye el bit de difusión / multidifusión. Los 23 bits inferiores de la dirección IP de multidifusión de 28 bits se asignan a los 23 bits del espacio de direcciones Ethernet disponibles.


Para vincular un socket UDP cuando se recibe multidifusión significa que se debe especificar una dirección y un puerto desde el que recibir datos (NO una interfaz local, como es el caso del enlace del aceptador de TCP). La dirección especificada en este caso tiene un rol de filtro , es decir, el socket solo recibirá los datagramas enviados a esa dirección y puerto de multidifusión, sin importar a qué grupos se una posteriormente el socket. Esto explica por qué al enlazar a INADDR_ANY (0.0.0.0) recibí datagramas enviados a mi grupo de multidifusión, mientras que al enlazar a cualquiera de las interfaces locales no recibí nada, aunque los datagramas se enviaban a la red a la que esa interfaz correspondido

Citando de UNIX® Network Programming Volume 1, Tercera edición: The Sockets Networking API de WR Stevens. 21.10. Enviando y recibiendo

[...] Queremos que el socket de recepción vincule el grupo y puerto de multidifusión, digamos 239.255.1.2 puerto 8888. (Recuerde que podríamos unir la dirección IP y el puerto 8888, pero el enlace de la dirección de multidifusión evita que el socket reciba cualquier otro datagrama que pueda llegar destinado al puerto 8888.) Entonces queremos que el socket receptor se una al grupo multicast. El socket de envío enviará datagramas a esta misma dirección y puerto de multidifusión, digamos 239.255.1.2 puerto 8888.


También es muy importante distinguir un zócalo de multidifusión de ENVÍO de un zócalo de multidifusión RECEPTOR.

Estoy de acuerdo con todas las respuestas anteriores con respecto a RECEIVING sockets de multidifusión. El OP notó que enlazar RECIBIR skt a una interfaz no ayudó. Sin embargo, es necesario vincular el conector de ENVÍO de multidifusión a una interfaz.

Para un socket de multidifusión SENDING en un servidor multi-homed, es muy importante crear un socket separado para cada interfaz que desee enviar. Debería crearse un sknt SENDING encuadernado para cada interfaz.

// 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) ); }

Sin esta solución, el envío de multidifusión recibirá intermitentemente sendto () errno ''No route to host''. Si alguien puede arrojar luz sobre por qué desenchufar una puerta de enlace DHCP hace que los zócalos de ENVÍO multidifusión Mac OS X se confundan, me encantaría escucharlo.