UDP multicasting with Python


Welcome to the next pikoTutorial!

The minimal receiver

Python
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

Python
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:

Bash
python3 receiver.py -mc_address 224.0.0.1:5000

And the sender with command:

Bash
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:

Bash
python3 receiver.py -mc_address 224.0.0.1:5000

And the second group of receivers with command:

Bash
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:

Bash
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:

Bash
python3 receiver.py -mc_address 224.0.0.1:5000

And the second group of receivers with command:

Bash
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):

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