UDP multicasting with Python


Welcome to the next pikoTutorial!

The minimal receiver

import struct
from argparse import ArgumentParser
from socket import socket, AF_INET, SOCK_DGRAM, inet_aton, IPPROTO_IP, \
    IP_ADD_MEMBERSHIP, INADDR_ANY, SOL_SOCKET, SO_REUSEADDR
# define command line interface
parser = ArgumentParser(description='UDP Multicast Receiver')
parser.add_argument('-mc_address', help='Multicast address')
# parse command line options
args = parser.parse_args()
# create a UDP socket
with socket(AF_INET, SOCK_DGRAM) as receiver_socket:
    # on the socket level, set the ability to reuse address
    receiver_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    # bind the socket to the receiver's own address and port
    multicast_ip = args.mc_address.split(':')[0]
    multicast_port = args.mc_address.split(':')[1]
    receiver_socket.bind((multicast_ip, int(multicast_port)))
    # set up the multicast group membership by converting addresses to
    # 4sl (4 byte string and 4 byte long integer)
    multicast_request = struct.pack("4sl", inet_aton(multicast_ip), INADDR_ANY)
    # join the multicast group
    receiver_socket.setsockopt(IPPROTO_IP, IP_ADD_MEMBERSHIP, multicast_request)
    print(f'Receiver listening on {multicast_ip}:{multicast_port}')
    # receive messages (in this case, indefinitely until interrupted)
    data, sender_address = receiver_socket.recvfrom(1024)  # Buffer size 1024 bytes
    print(f'Received message from {sender_address[0]}:{sender_address[1]} : {data.decode()}')

Note for beginners: do not remove line 14 from the receiver implementation! Without the ability to re-use the same address, you will not be able to run more than 1 multicast receiver and you’ll end up with a simple UDP client/server setup.

The minimal sender

import time
from argparse import ArgumentParser
from socket import socket, AF_INET, SOCK_DGRAM, IPPROTO_UDP
# define command line interface
parser = ArgumentParser()
parser.add_argument('-sender_address',
                    help='Sender\'s source address')
parser.add_argument('-mc_address', nargs='+',
                    help='List of multicast addresses separated by space')
# parse command line options
args = parser.parse_args()
# create a UDP socket
with socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) as sender_socket:
    # assign sender specific source port
    sender_ip: str = args.sender_address.split(':')[0]
    sender_port: int = int(args.sender_address.split(':')[1])
    sender_socket.bind((sender_ip, sender_port))
    # send the message to each multicast address in the list
    for address in args.mc_address:
        multicast_ip: str = address.split(':')[0]
        multicast_port: int = int(address.split(':')[1])
        sender_socket.sendto(f'This message is for {multicast_ip}:{multicast_port} receivers'.encode(),
                             (multicast_ip, multicast_port))

Multicast on a single IP and a single port

The simplest scenario assumes 1 sender sending from 127.0.0.1:3000 address to 2 receivers listening on the shared 224.0.0.1:5000 multicast address:

UDP multicast single port receivers

To achieve that, run as many receivers as you want with command:

python3 receiver.py -mc_address 224.0.0.1:5000

And the sender with command:

python3 sender.py -sender_address 127.0.0.1:3000 -mc_address 224.0.0.1:5000

Note for beginners: multicast IPs can range from 224.0.0.0 to 239.255.255.255.

Multicast on a single IP and multiple ports

In this setup 1 sender sends message from 127.0.0.1:3000 address to a single multicast IP and two multicast ports: 224.0.0.1:5000 and 224.0.0.1:5001.

UDP multicasting multiple port

Run first group of receivers with command:

python3 receiver.py -mc_address 224.0.0.1:5000

And the second group of receivers with command:

python3 receiver.py -mc_address 224.0.0.1:5001

After that you can send a multicast messages to all of them running sender with the following command:

python3 sender.py -sender_address 127.0.0.1:3000 -mc_address 224.0.0.1:5000 224.0.0.1:5001

Multicast on multiple IPs

Groups of receivers can be of course differentiated also by multicast IP.

UDP multicast on multiple IPs

Note for beginners: in this example I decided to not specify the sender’s IP. In the previous examples, I used loopback interface on which 127.0.0.1 IP is always available, but multiple multicast IPs on that interface may not work on your machine without additional configuration. I don’t know what are real network interfaces available on your machine and what IPs are assigned on them, so I let the operating system choose it automatically by leaving the sender’s IP empty.

For that, run the first group of receivers with command:

python3 receiver.py -mc_address 224.0.0.1:5000

And the second group of receivers with command:

python3 receiver.py -mc_address 224.0.0.2:5000

Now the only thing left to do is to launch the multicast sender (leaving the IP empty – see the note above):

python3 sender.py -sender_address :3000 -mc_address 224.0.0.1:5000 224.0.0.2:5000