Basing strictly on:
How do you UDP multicast in Python?
import socket
import struct
MCAST_GRP = '233.3.3.3'
MCAST_PORT = 12312
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:
print sock.recv(10240)
I stream video using VLC.
However I can bind using only IS_ALL_GROUPS = True. Any time I would like to bind by IS_ALL_GROUPS = False I got WinError 10049: The requested address is not valid in its context.
When I try to use the same sender and receiver scripts but on the linux there is no such error.
What can I do in this situation?
Basing on MC receiver from stack:
How do you UDP multicast in Python?
I would like to completely understand what is going on. Here is what I understand and what not:
As far as I understand: socket_name = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) => creating socket with IP proto ver 4, that will receive MC datagrams using UDP.
socket_name.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) this is responsible for setting parameter that socket could use the same address
(and in this case, port, because SO_REUSEADDR = SO_REUSEPORT for Multicasts).
(Paragraph multicast ~ How do SO_REUSEADDR and SO_REUSEPORT differ?)
if IS_ALL_GROUPS: socket_name.bind(('', MCAST_PORT)) means that if IS_ALL_GROUPS is true, bind socket to any addres, and else: socket_name.bind((MCAST_GRP, MCAST_PORT)) means bind socket to specific given IP address.
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY) means convert IP address to binary form and socket_name.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) means that we add membership of group and after this line packets starting to arrive.
However I don't understand some things.
Why when I used IS_ALL_GROUPS = true where MCAST_GRP = '239.0.1.104' program could start and when the parameter was false, it didn't bound to specific multicast address? My logic is that when the parameter is true it binds to any MCast addres he gets from IGMP join messages and when parameter is false, it binds to specific given address. Am I correct?
I have multithread program that analyses bitrate to which i provide more than one address in form of list. When I set IS_ALL_GROUPS to false, program works correctly printing out e.g. 10.5, 4.5, 5.0 where each result is bitrate of one stream from unique address, however all of addresses share the same port 12345. When I set IS_ALL_GROUPS to true, program sums up results giving 20.0, 20.0, 20.0, do you know what may be the cause?
import socket
import struct
MCAST_GRP = '239.0.1.104'
MCAST_PORT = 12345
IS_ALL_GROUPS = True
socket_name = 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
socket_name.bind(('', MCAST_PORT))
else:
# on this port, listen ONLY to MCAST_GRP
socket_name.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:
print socket_name.recv(10240)
I think there are a few things going on:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
should probably be:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
the only "datagram" based protocol for IPv4 is UDP, the protocol field is just for "raw sockets" which are used by programs like tcpdump. this shouldn't matter though.
next:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
just allows multiple sockets to be bound to this same (addr, port) combination at once, i.e. you can run multiple copies of your program at once, or in quick succession. which I think is what you're saying
next:
sock.bind((host, port))
says you want to receive packets destined for the a given address and port. the special address '' (i.e. INADDR_ANY) means that the kernel shouldn't do any filtering on address. you probably don't want to bind to any specific address at this stage, see What does it mean to bind a multicast (UDP) socket?
next:
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
I'd suggest using "=4sl" for the format string as this gives me 8 bytes, rather than 16, and is consistent with a C struct ip_mreq on my (Linux) system. this difference in padding might just be a 32/64bit issue, and doesn't seem to hurt
to summarise: 1. the setsockopt(IP_ADD_MEMBERSHIP) call has asked your kernel's network stack to arrange (via IGMP) for multicast packets sent to MCAST_GRP to be delivered to a network interface attached to your host. 2. the bind() call has arranged for packets received via the network interface to make it to the socket in your process. it might be important that the bind() specifies INADDR_ANY so that all multicast packets that arrive are delivered to your process rather than being filtered
a couple of command lines that might be useful are:
tcpdump -i eth0 -vv 'ip proto 2'
which dumps IGMP packets, and:
tcpdump -i eth0 -vv 'net 224.0.0.0/4'
which dumps multicast packets
I'm not sure what your "multithread analysis program" is doing, as you've not given any relevant info. your posted code is also wrong, presumably socket_name and sock are the same? I'd also suggest using Python 3 as Python 2 is dying
i'm trying to implement the following program in Python to send join request from a specific interface (right now my kernel is choosing something by default).
import socket
import struct
import time
MCAST_GRP = '239.0.1.1'
MCAST_PORT = 2000
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
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)
i was trying to change the socket.INADDR_ANY to socket.inet_aton('X.X.X.X')
when X.X.X.X is my interface ip address represent by a string, but i got an error.
Any way what can i do to make it work?
Thanks, Talor
found the answer:
need to change "4sl" to "4s4s" and express the interface that i want to send the igmp join request as a four-bytes string. i can write the code as follow:
interface = '168.152.63.15'
mreq = struct.pack("4s4s", socket.inet_aton(MCAST_GRP), socket.inet_aton(interface))
When I send UDP multicast from Windows vm (Parallels) and read it on my OS X El Capitan host with recvfrom, the "sender" info returned by recvfrom matches the adaptor it was received on on the mac and not the adaptor it was sent from on the windows machine like it was supposed to.
Also, if I run the same code to send UDP packets from OS X they won't be received on the Windows vm. But sending between two Windows VM's works perfectly, and the "sender" info is returned properly.
Maybe I need to bind the OS X sender to a specific interface? It would be good to send over all interfaces if possible. I'm not sure if this is a networking problem, or a parallels problem, or some other problem.
Windows:: 10.211.5.8
OS X: 192.168.1.65, 10.211.55.2, 10.37.129.2
sender:
import socket, struct
addrinfo = socket.getaddrinfo('225.0.0.250', None)[0]
s = socket.socket(addrinfo[0], socket.SOCK_DGRAM)
ttl_bin = struct.pack('#i', MYTTL)
s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl_bin)
data = repr(time.time()) + '\0'
s.sendto(data.encode('utf-8'), (addrinfo[4][0], MYPORT))
receiver:
import socket, struct
addrinfo = socket.getaddrinfo('225.0.0.250', None)[0]
s = socket.socket(addrinfo[0], socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('', MYPORT))
group_bin = socket.inet_pton(addrinfo[0], addrinfo[4][0])
mreq = group_bin + struct.pack('=I', socket.INADDR_ANY)
s.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, more)
data, sender = s.recvfrom(1500)
print(str(sender) + ' ' + repr(data))
Windows vm sender -> OS X receiver ('192.168.1.65' is receiver):
('192.168.1.65', 62958) b'1462856664.784149\x00'
('192.168.1.65', 62958) b'1462856665.7842436\x00'
('192.168.1.65', 62958) b'1462856666.7843592\x00'
('192.168.1.65', 62958) b'1462856667.7846606\x00'
Windows vm sender -> Windows vm receiver ('10.211.55.3' is sender):
('10.211.55.3', 54936) b'1462856959.228\x00'
('10.211.55.3', 54936) b'1462856960.242\x00'
('10.211.55.3', 54936) b'1462856961.256\x00'
('10.211.55.3', 54936) b'1462856962.27\x00'
Thank you!
How do you send and receive UDP multicast in Python? Is there a standard library to do so?
This works for me:
Receive
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))
It is based off the examples from http://wiki.python.org/moin/UdpCommunication which didn't work.
My system is...
Linux 2.6.31-15-generic #50-Ubuntu SMP Tue Nov 10 14:54:29 UTC 2009 i686 GNU/Linux
Python 2.6.4
Multicast sender that broadcasts to a multicast group:
#!/usr/bin/env python
import socket
import struct
def main():
MCAST_GRP = '224.1.1.1'
MCAST_PORT = 5007
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 32)
sock.sendto('Hello World!', (MCAST_GRP, MCAST_PORT))
if __name__ == '__main__':
main()
Multicast receiver that reads from a multicast group and prints hex data to the console:
#!/usr/bin/env python
import socket
import binascii
def main():
MCAST_GRP = '224.1.1.1'
MCAST_PORT = 5007
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
try:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
except AttributeError:
pass
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 32)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 1)
sock.bind((MCAST_GRP, MCAST_PORT))
host = socket.gethostbyname(socket.gethostname())
sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(host))
sock.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP,
socket.inet_aton(MCAST_GRP) + socket.inet_aton(host))
while 1:
try:
data, addr = sock.recvfrom(1024)
except socket.error, e:
print 'Exception'
else:
hexdata = binascii.hexlify(data)
print 'Data = %s' % hexdata
if __name__ == '__main__':
main()
Better use:
sock.bind((MCAST_GRP, MCAST_PORT))
instead of:
sock.bind(('', MCAST_PORT))
because, if you want to listen to multiple multicast groups on the same port, you'll get all messages on all listeners.
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:
from socket import *
multicast_port = 55555
multicast_group = "224.1.1.1"
interface_ip = "10.11.1.43"
s = socket(AF_INET, SOCK_DGRAM )
s.bind(("", multicast_port ))
mreq = inet_aton(multicast_group) + inet_aton(interface_ip)
s.setsockopt(IPPROTO_IP, IP_ADD_MEMBERSHIP, str(mreq))
while 1:
print s.recv(1500)
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
see What does it mean to bind a multicast (UDP) socket? for more on how multicast works
Multicast receiver:
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))
python3 multicast_recv.py --iface='192.168.56.102' --join-mcast-groups '224.1.1.1' '224.1.1.2' '224.1.1.3' --bind-group '224.1.1.2'
python3 multicast_recv.py --iface='192.168.56.102' --join-mcast-groups '224.1.1.4'
Multicast sender:
import socket
import argparse
def run(group, port):
MULTICAST_TTL = 20
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, MULTICAST_TTL)
sock.sendto(b'from multicast_send.py: ' +
f'group: {group}, port: {port}'.encode(), (group, port))
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--mcast-group', default='224.1.1.1')
parser.add_argument('--port', default=19900)
args = parser.parse_args()
run(args.mcast_group, args.port)
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
python3 multicast_send.py --mcast-group '224.1.1.2'
python3 multicast_send.py --mcast-group '224.1.1.4'
Have a look at py-multicast. Network module can check if an interface supports multicast (on Linux at least).
import multicast
from multicast import network
receiver = multicast.MulticastUDPReceiver ("eth0", "238.0.0.1", 1234 )
data = receiver.read()
receiver.close()
config = network.ifconfig()
print config['eth0'].addresses
# ['10.0.0.1']
print config['eth0'].multicast
#True - eth0 supports multicast
print config['eth0'].up
#True - eth0 is up
Perhaps problems with not seeing IGMP, were caused by an interface not supporting multicast?
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:
import socket
import struct
MCAST_GRP = '224.1.1.1'
MCAST_PORT = 5007
ttl = struct.pack('B', 2)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl)
sock.sendto("robot", (MCAST_GRP, MCAST_PORT))
GumD and GumC https://github.com/futzu/gumd
I use GumD mostly for video, but you can use any kind of file.
pip3 install gumd
gumd (Daemon)
>>>> from gumd import GumD
>>>> gumd =GumD('235.35.3.5:3535',1)
>>>> gumd.mcast("/home/a/stuff.txt")
# Can also use http(s), UDP and multicast
stream uri: udp://#235.35.3.5:3535
>>>>
Use gumc (Client)
>>>> from gumc import GumC
>>>> gumc = GumC("udp://#235.35.3.5:3535")
>>>> data = gumc.read(8)
>>>> data
b'Helloooo'
tolomea's answer worked for me. I hacked it into socketserver.UDPServer too:
class ThreadedMulticastServer(socketserver.ThreadingMixIn, socketserver.UDPServer):
def __init__(self, *args):
super().__init__(*args)
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind((MCAST_GRP, MCAST_PORT))
mreq = struct.pack('4sl', socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)