Soy un ingeniero de software que diseña un software distribuido, en el que el procedimiento de inicio utiliza multidifusión IP para descubrir sus pares. El software en sí se distribuye como diferentes módulos ejecutables y, como tal, a veces tiene sentido ejecutar varios módulos ejecutables en el mismo host. Aquí es donde comienzan mis problemas, ya que me parece que Windows no es bueno para gestionar varios procesos suscribiéndose a un único grupo de multidifusión.
Mi objetivo es poder elegir libremente en qué hosts inicio qué ejecutables, y estos descubrirán todos sus pares (ya sea en el mismo host o en otros hosts) sin ningún conocimiento predefinido.
Hasta ahora, la solución de problemas indica que el problema es cómo Windows maneja el escenario de múltiples procesos suscribiéndose a los mismos grupos de multidifusión, porque:
Si se utiliza el mismo proceso para enviar y recibir datagramas de multidifusión en la misma máquina, funciona como se esperaba. El proceso se puede dividir en varios subprocesos sin ningún problema.
Si ejecuto diferentes procesos para enviar y recibir, el proceso de recepción no recibe nada, a pesar del mensaje de unión al grupo y todos los datagramas aparecen en Wireshark.
El escenario descrito en 2 funciona si antes de escuchar también uso el mismo socket para enviar un paquete al grupo de multidifusión al que me he unido. Luego, este último recibe datagramas de multidifusión durante un período de tiempo no especificado, después del cual simplemente deja de recibir datagramas (sigue esperando que lleguen los datagramas). Se confirma que los datagramas se envían hacia/desde la red mediante Wireshark.
Mis últimos hallazgos indican que si envío periódicamente un mensaje de multidifusión al grupo al que me suscribo, recibo datagramas enviados a este grupo de multidifusión, también desde otros procesos en el mismo host.
La multidifusión IP, según tengo entendido, sólo define la comunicación entreHospedadores, mientras que es responsabilidad del sistema operativo redirigir los paquetes entrantes al lugar apropiadosolicitud. Como los paquetes parecen aparecer siempre en Wireshark, incluso si una aplicación no los recibe, parece que Windows falla en el manejo de los paquetes entrantes, o al menos en entregarlos a las aplicaciones apropiadas.
Agradecería que alguien pudiera confirmar o rechazar mi razonamiento, así como indicarme la dirección correcta sobre cómo resolver este problema. El objetivo es poder que varias aplicaciones en el mismo host se unan a un único canal de grupo de multidifusión y también reciban mensajes sin necesidad de enviar también "basura" al grupo de multidifusión para que puedan recibir datos (la solución alternativa se describe como número de punto 4).
Estoy usando Java para la implementación y, si lo solicita, puedo publicar un MWE aquí. Sin embargo, me temo que puede cambiar el enfoque del escenario a la programación, lo cual no es la preocupación aquí (por lo que puedo deducir).
Respuesta1
El OP solicita un método para descubrir pares en una red sin conocimiento de esos pares, segúnesta fuentemDNS requiere al menos conocimiento del nombre de host de cada par.
Como resultado, mDNS no es una solución viable para este problema como se describe.
De acuerdo aa esta respuesta, sobre multicast, lo que has pedido es posible. Implica utilizar la opción de socket SO_REUSEADDR. Cuando utiliza esta opción de socket, permite que varios sockets escuchen en la misma combinación de dirección: puerto, siempre que;
- La dirección es una dirección de multidifusión; y
- SO_RESUSEADDR se establece en todos y cada uno de los sockets que intentan vincularse a la combinación dirección:puerto.
Sin embargo, segúnesta respuesta, es posible que también necesites configurar SO_BROADCAST en cada socket, ¡lo cual ciertamente no es intuitivo! Sin embargo, en mis propias pruebas usando Python en Windows, NO necesitaba hacer esto, así que no estoy seguro de qué tan legítima sea esa respuesta, la menciono en caso de que te quedes atascado, aunque trato esto como potencialmente desactualizado, estoy probando en Windows 10 en este momento.
Aquí hay un ejemplo de código que he probado que no produce errores; es parte de una prueba automatizada que utiliza balizas para conectar un proceso de trabajo a un proceso de intermediario. Esta prueba pasa cuando se ejecuta en un solo nodo, aún no la he probado en varios nodos
import socket, struct
MCAST_GRP = ''
MCAST_PORT = 1234
MULTICAST_TTL = 2
def create_beacon_listener():
sock = socket.socket(
socket.AF_INET,
socket.SOCK_DGRAM,
socket.IPPROTO_UDP
)
sock.setsockopt(
socket.SOL_SOCKET,
socket.SO_REUSEADDR,
1
)
sock.setblocking(0)
sock.bind(( MCAST_GRP, MCAST_PORT ))
multicast_grp_req = struct.pack(
"4sl",
socket.inet_aton(MCAST_GRP),
socket.INADDR_ANY
)
sock.setsockopt(
socket.IPPROTO_IP,
socket.IP_ADD_MEMBERSHIP,
multicast_grp_req
)
return sock
El código anterior devuelve un socket de escucha en la dirección de grupo deseada MCAST_GRP y el puerto MCAST_PORT. Y sí, MCAST_GRP está configurado en una cadena en blanco, lo que significa que realmente escucha en todos los grupos de direcciones de multidifusión. Esto parece ser una limitación en Windows o, según mi conocimiento, probablemente en este último.
De cualquier manera, usar una dirección específica en la variable MCAST_GRP resultará en un error, por lo que usar una cadena en blanco al menos obtiene el código para resolver el problema descrito por el OP.
Este código se puede reutilizar varias veces dentro del mismo proceso, aunque todavía no he probado si varios procesos pueden usarlo correctamente. Probé usando el marco asyncio de Python, por lo que varios sockets escuchan en el mismo hilo.
Además, para compatibilidad multiplataforma, también puede hacer que este código funcione en sistemas operativos nix, y en esos sistemas puede configurar MCAST_GRP en una dirección específica.
EDITAR:
Hace poco me encontréeste recurso de MSque ofrece un ejemplo que parece permitir el uso de una dirección de grupo de multidifusión específica en lugar de vincularse a todos. Implica configurar algunas opciones más en la variable multicast_grp_req. Además, el ejemplo es código C++, no Python, pero son lo suficientemente similares como para entender lo esencial.
Espero que esto ayude :)