import socket
import struct
MCAST_GRP = '224.1.1.1'
MCAST_PORT = 5007
IS_ALL_GROUPS = True
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if IS_ALL_GROUPS:
# on this port, receives ALL multicast groups
sock.bind(('', MCAST_PORT))
else:
# on this port, listen ONLY to MCAST_GRP
sock.bind((MCAST_GRP, MCAST_PORT))
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
while True:
# For Python 3, change next line to "print(sock.recv(10240))"
print sock.recv(10240)
Send
import socket
MCAST_GRP = '224.1.1.1'
MCAST_PORT = 5007
# regarding socket.IP_MULTICAST_TTL
# ---------------------------------
# for all packets sent, after two hops on the network the packet will not
# be re-sent/broadcast (see https://www.tldp.org/HOWTO/Multicast-HOWTO-6.html)
MULTICAST_TTL = 2
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, MULTICAST_TTL)
# For Python 3, change next line to 'sock.sendto(b"robot", ...' to avoid the
# "bytes-like object is required" msg (https://stackoverflow.com/a/42612820)
sock.sendto("robot", (MCAST_GRP, MCAST_PORT))
To make the client code (from tolomea) work on Solaris you need to pass the ttl value for the IP_MULTICAST_TTL socket option as an unsigned char. Otherwise you will get an error.
This worked for me on Solaris 10 and 11:
In order to Join multicast group Python uses native OS socket interface. Due to portability and stability of Python environment many of socket options are directly forwarded to native socket setsockopt call. Multicast mode of operation such as joining and dropping group membership can be accomplished by setsockopt only.
Basic program for receiving multicast IP packet can look like:
Firstly it creates socket, binds it and triggers triggers multicast group joining by issuing setsockopt. At very end it receives packets forever.
Sending multicast IP frames is straight forward. If you have single NIC in your system sending such packets does not differ from usual UDP frames sending. All you need to take care of is just set correct destination IP address in sendto() method.
I noticed that lot of examples around Internet works by accident in fact. Even on official python documentation. Issue for all of them are using struct.pack incorrectly. Please be advised that typical example uses 4sl as format and it is not aligned with actual OS socket interface structure.
I will try to describe what happens underneath the hood when exercising setsockopt call for python socket object.
Python forwards setsockopt method call to native C socket interface. Linux socket documentation (see man 7 ip) introduces two forms of ip_mreqn structure for IP_ADD_MEMBERSHIP option. Shortest is form is 8 bytes long and longer is 12 bytes long. Above example generates 8 byte setsockopt call where first four bytes define multicast_group and second four bytes define interface_ip.
Just another answer to explain some subtle points in the code of the other answers:
socket.INADDR_ANY - (Edited) In the context of IP_ADD_MEMBERSHIP, this doesn't really bind the socket to all interfaces but just choose the default interface where multicast is up (according to routing table)
Joining a multicast group isn't the same as binding a socket to a local interface address
import socket
import struct
import argparse
def run(groups, port, iface=None, bind_group=None):
# generally speaking you want to bind to one of the groups you joined in
# this script,
# but it is also possible to bind to group which is added by some other
# programs (like another python program instance of this)
# assert bind_group in groups + [None], \
# 'bind group not in groups to join'
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
# allow reuse of socket (to allow another instance of python running this
# script binding to the same ip/port)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('' if bind_group is None else bind_group, port))
for group in groups:
mreq = struct.pack(
'4sl' if iface is None else '4s4s',
socket.inet_aton(group),
socket.INADDR_ANY if iface is None else socket.inet_aton(iface))
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
while True:
print(sock.recv(10240))
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--port', type=int, default=19900)
parser.add_argument('--join-mcast-groups', default=[], nargs='*',
help='multicast groups (ip addrs) to listen to join')
parser.add_argument(
'--iface', default=None,
help='local interface to use for listening to multicast data; '
'if unspecified, any interface would be chosen')
parser.add_argument(
'--bind-group', default=None,
help='multicast groups (ip addrs) to bind to for the udp socket; '
'should be one of the multicast groups joined globally '
'(not necessarily joined in this python program) '
'in the interface specified by --iface. '
'If unspecified, bind to 0.0.0.0 '
'(all addresses (all multicast addresses) of that interface)')
args = parser.parse_args()
run(args.join_mcast_groups, args.port, args.iface, args.bind_group)
sample usage: (run the below in two consoles and choose your own --iface (must be same as the interface that receives the multicast data))
sample usage: # assume the receiver binds to the below multicast group address and that some program requests to join that group. And to simplify the case, assume the receiver and the sender are under the same subnet