Multidifusión

Las conexiones punto a punto manejan muchas necesidades de comunicación, pero pasar la misma información entre muchos compañeros se convierte en un desafío a medida que el número de conexiones directas crece. Enviar mensajes por separado a cada destinatario consume tiempo de procesamiento y ancho de banda adicional, que puede ser un problema para aplicaciones como la transmisión de vídeo o audio. Usar multidifusión para entregar mensajes a más de un punto final a la vez logra una mejor eficiencia porque la infraestructura de red asegura que los paquetes se entreguen a todos destinatarios.

Los mensajes de multidifusión siempre se envían utilizando UDP, ya que TCP asume un par de sistemas de comunicación. Las direcciones para multidifusión llamadas grupos de multidifusión, son un subconjunto del rango de direcciones IPv4 regulares (224.0.0.0 hasta 230.255.255.255) reservado para el tráfico multidifusión. Estas direcciones son tratadas especialmente por enrutadores y conmutadores de red, para que los mensajes enviados al grupo se puedan distribuir a través de Internet a todos los destinatarios que se han unido al grupo.

Nota

Algunos conmutadores y enrutadores administrados tienen el tráfico de multidifusión desactivado por defecto. Si tienes problemas con los programas de ejemplo, consulta la configuración de tu hardware de red.

Enviar mensajes de multidifusión

Este cliente de eco modificado enviará un mensaje a un grupo de multidifusión, luego informa todas las respuestas que recibe. Como no tiene forma de saber cuántas respuestas esperar, usa un valor de tiempo de espera en el conector para evitar el bloqueo indefinido mientras se espera una respuesta.

El conector también debe configurarse con un valor de tiempo de vida (TTL) para los mensajes. El TTL controla cuántas redes recibirán el paquete. Establece el TTL con la opción IP_MULTICAST_TTL y setsockopt(). El valor predeterminado, 1, significa que los paquetes no son reenviados por el enrutador más allá del segmento de red actual. El valor puede variar hasta 255, y debe estar empaquetado en un byte único

socket_multicast_sender.py
import socket
import struct
import sys

message = b'very important data'
multicast_group = ('224.3.29.71', 10000)

# Create the datagram socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# Set a timeout so the socket does not block
# indefinitely when trying to receive data.
sock.settimeout(0.2)

# Set the time-to-live for messages to 1 so they do not
# go past the local network segment.
ttl = struct.pack('b', 1)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl)

try:

    # Send data to the multicast group
    print('sending {!r}'.format(message))
    sent = sock.sendto(message, multicast_group)

    # Look for responses from all recipients
    while True:
        print('waiting to receive')
        try:
            data, server = sock.recvfrom(16)
        except socket.timeout:
            print('timed out, no more responses')
            break
        else:
            print('received {!r} from {}'.format(
                data, server))

finally:
    print('closing socket')
    sock.close()

El resto del remitente se parece al cliente de eco UDP, excepto que espera respuestas múltiples, así que usa un bucle para llamar a recvfrom() hasta que se agote el tiempo.

Recibir de mensajes de multidifusión

El primer paso para establecer un receptor de multidifusión es crear el conector UDP. Después de crear el conector normal y enlazarlo a un puerto, se puede agregar al grupo de multidifusión usando setsockopt() para cambiar la opción IP_ADD_MEMBERSHIP. El valor de opción es el representación empaquetada de 8 bytes de la dirección del grupo de multidifusión seguida por la interfaz de red en la que el servidor debe escuchar el tráfico, identificado por su dirección IP. En este caso, el receptor escucha en todas las interfaces usando INADDR_ANY.

socket_multicast_receiver.py
import socket
import struct
import sys

multicast_group = '224.3.29.71'
server_address = ('', 10000)

# Create the socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# Bind to the server address
sock.bind(server_address)

# Tell the operating system to add the socket to
# the multicast group on all interfaces.
group = socket.inet_aton(multicast_group)
mreq = struct.pack('4sL', group, socket.INADDR_ANY)
sock.setsockopt(
    socket.IPPROTO_IP,
    socket.IP_ADD_MEMBERSHIP,
    mreq)

# Receive/respond loop
while True:
    print('\nwaiting to receive message')
    data, address = sock.recvfrom(1024)

    print('received {} bytes from {}'.format(
        len(data), address))
    print(data)

    print('sending acknowledgement to', address)
    sock.sendto(b'ack', address)

El bucle principal para el receptor es igual que el servidor eco UDP regular.

Salida ejemplo

Este ejemplo muestra el receptor de multidifusión ejecutándose en dos hosts. A tiene la dirección 192.168.1.13 y B tiene la dirección 192.168.1.14.

[A]$ python3 socket_multicast_receiver.py

waiting to receive message
received 19 bytes from ('192.168.1.14', 62650)
b'very important data'
sending acknowledgement to ('192.168.1.14', 62650)

waiting to receive message

[B]$ python3 source/socket/socket_multicast_receiver.py

waiting to receive message
received 19 bytes from ('192.168.1.14', 64288)
b'very important data'
sending acknowledgement to ('192.168.1.14', 64288)

waiting to receive message

El remitente se está ejecutando en el host B.

[B]$ python3 socket_multicast_sender.py
sending b'very important data'
waiting to receive
received b'ack' from ('192.168.1.14', 10000)
waiting to receive
received b'ack' from ('192.168.1.13', 10000)
waiting to receive
timed out, no more responses
closing socket

El mensaje se envía una vez, y dos acuses de recibo de los mensajes salientes se reciben mensajes, uno de cada host A y B.

Ver también