Multicast listing between two computer is NOT working - python

I am trying to write a listener , which will listen to multicast data from different systems. As per my understanding, any system which is present in the same subnet will be able to get any data that will be sent to a particular muticast_grp and multicast port. But in my case, the below code is working fine for any data which will be sent to the same PC, but is NOT able to capture the data which will be sent from another PC.
I am able to see the data in Wireshark. But not in the reciver.
I don't have control over the server(sender)
import socket
import struct
import sys
MCAST_GRP = '239.255.255.250'
MCAST_PORT = 3702
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)
sock.bind(('', 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))

You have to listen on host '0.0.0.0', but send to the reciver IP
example
i want to send string "hi" to computer with ip "1.1.1.1"
then for sending I use
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
s.sendto(bytes("hi", 'utf-8'), (1.1.1.1, 3702))
and to recive
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.bind(('0.0.0.0', 3702))
print(sock.recv(3))

Related

Is there a way to distinguish unicast and multicast when receiving UDP datagram from same socket?

I have an application that meeds to:
talks in UDP
support optionally disabling multicast.
treat multicast message the same way as unicast message but with few restrictions
I thought I should use same UDP socket to receive both unicast and multicast datagrams.
# udp_multicast_recv.py
# Python version used: 3.9.1
import socket
import struct
host = "224.1.1.1"
# host = "0.0.0.0"
listen_all = True
port = 5683
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
if listen_all:
s.bind(("", port))
else:
s.bind((host, port))
mreq = struct.pack("4sl", socket.inet_aton(host), socket.INADDR_ANY)
s.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
data = None
print("Listening")
while True:
# data, addr = s.recvfrom(1024)
data, ancdata, msg_flags, addr = s.recvmsg(1024)
print(addr, ":", data, ancdata, msg_flags)
# udp_multicast_send.py
import socket
dest = "224.1.1.1"
dest = "192.168.86.188"
port = 5683
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.sendto("Hello world".encode(), (dest, port))
I soon noticed I need a way to distinguish multicast datagram from unicast datagram. But I was unable to find a solution nor related question on how to achieve this on the internet.
Hence the question: When receiving UDP datagram from same socket, is there a way to tell whether datagram is unicast or multicast?

Sending broadcast in Python

I am trying to learn to code sockets (in Python 3), I am simply trying to send a broadcast from a server and receive it from a client.
My problem is that whenever I try to send the packets to 255.255.255.255, it seems nothing is actually sent.
I tried to find the packets with wireshark but except on the loopback interface, I can't find any.
I can successfully send a message between the two computers when manually inputting the IP, and I also see the packets in wireshark.
Here's the code for the client
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
sock.bind(("0.0.0.0", 5005))
while True:
# sock.sendto(bytes("hello", "utf-8"), ip_co)
data, addr = sock.recvfrom(1024)
print(data)
And here's the code for the server
import socket
from time import sleep
def main():
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # UDP
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
# sock.settimeout(2)
while True:
sock.sendto(bytes("test", "utf-8"), ("255.255.255.255", 5005))
sleep(1)
main()
Sorry if the code is ugly, I am very new to sockets and to python.
The reason is that you are broadcasting on one interface and listening on another. See this answer UDP-Broadcast on all interfaces.
You need to broadcast on all interfaces, for example using the following (purely for demonstration) code. However, keep in mind that broadcasting over IP is a legacy feature which has been dropped from IPv6. Use multicasting instead.
import socket
from time import sleep
def main():
interfaces = socket.getaddrinfo(host=socket.gethostname(), port=None, family=socket.AF_INET)
allips = [ip[-1][0] for ip in interfaces]
msg = b'hello world'
while True:
for ip in allips:
print(f'sending on {ip}')
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # UDP
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
sock.bind((ip,0))
sock.sendto(msg, ("255.255.255.255", 5005))
sock.close()
sleep(2)
main()
If you are here to just know how to send broadcast and the solution of Mario don't work for you, because you are getting list of ips as 127.0.1.1, so there is a simpler solution:
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) as sock:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
sock.sendto(msg, ("255.255.255.255", 5005))
Notice that the disadvantage here is that it will not go throw all the network interfaces, like Mario solution, just the one per the OS IP table. So keep in mind...

How to fix sock.recv() blocking state in python

I am quite newbie in networking programming in python.
I use the script I found on the internet that that connects to multicast adress and recieve MPEG-TS packets. I see on wireshark that after sock.setsockopt command, MPEG-TS packets are arriving to my computer.
Screen from wireshark
https://imgur.com/XJDwd61
But the problem occurs when I want to print the sock.recv() result. I believe it's because of blocking state if I'm understanding the documentation properly. After uncommenting setblocking(0) I got 10035 error.
Do you have any clue what I need to add in order to print recieved data in terminal?
Thank you in advance.
I tried changing the buffer_size in sock.recv() to be less, equal and just how it is right now - above 1358 bytes which is amount of single datagram bytes.
import socket
import struct
import time
MCAST_GRP = '239.0.1.104'
MCAST_PORT = 12345
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)
#sock.setblocking(0)
print(f"Entering while loop")
while True:
time.sleep(1)
print(f"I'm in while loop")
print(sock.recv(4096))
Based on the example code from How do you UDP multicast in Python? when I try to run it on my system, it works fine. So, I changed it with your changes, this is the receiver.py code,
import socket
import struct
MCAST_GRP = '239.0.1.104'
MCAST_PORT = 12345
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)
print("Entering while loop")
while True:
print("I'm in while loop")
print(sock.recv(4096))
Notice, I slightly modified print statements and removed f as it was generating an error. And this is the sender.py code,
import socket
MCAST_GRP = '239.0.1.104'
MCAST_PORT = 12345
# 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)
sock.sendto(bytes("robot", 'UTF-8'), (MCAST_GRP, MCAST_PORT))
Notice that I am converting robot string into bytes with UTF-8 encoding, as this is necessary if using python3, it is not necessary in python2. This is my execution result,
As you can see when I invoke the sender, then the receiver does display robot string and loops through the while loop. It works fine.
In the case, if you are already receiving the multicast packets which you showed on your Wireshark, you need to ensure that those packets are being sent to the same port number on which you are listening, i-e 12345 in your case. If they are being sent to different port number [which is probably the case], then please change your receiver to listen on that port number to start receiving those packets.
Try this simple code, you can set less BUFF_SIZE if you want.
#BUFF_SIZE = 4096
BUFF_SIZE = 1024
data = b''
while True:
#chunk = s.recv(BUFF_SIZE)
#FOR UDP (#Saeed credits)
chuck = sock.recvfrom(BUFF_SIZE)
data += chunk
if len(chunk) < BUFF_SIZE:
break
print(data)

python multicast with windows 2012 server receiver fail

Python multicast receiver running on windows 2012 server is not getting udp traffic. I inspected traffic and I see the udp traffic is coming across. I have a firewall udp rule for the multicast group with edge traversal allowed. I get no firewall block events. This same code works with windows 2008 server client.
On server (linux ubuntu):
PORT = 12345
import sys, os, time, socket, struct
from socket import gethostname
mc_ttl = 1
mc_group = '224.7.7.7'
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(0.2)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL,
struct.pack('b', mc_ttl))
d = "my data"
while True:
sock.sendto(d, (mc_group, PORT))
on Client (windows 2012 server):
PORT = 12345
import sys, os, time, socket, struct
from socket import gethostname
mc_group = '224.7.7.7'
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', PORT))
mreq = struct.pack("4sl", socket.inet_aton(mc_group), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
while True:
print sock.recv(10240)
Note the above code works on windows 2008 server. After doing some searching I saw that theres some issues with INADDR_ANY binding to wrong address (and the requisite advice against binding to ''). Inspecting incoming udp traffic on windows client I see the multicast traffic but reciever doesn't get it.
I tried the following after reading this post. Which also did not work.
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
grp = socket.inet_aton(mc_group)
ip = socket.gethostbyname(socket.gethostname())
iface = socket.inet_aton(ip)
mreq = grp + iface
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
sock.bind((ip, PORT))
Anyone successfully implemented receiver on windows 2012 server?
I updated the response on the other post to be a bit more explicit. Using socket.gethostbyname() is still getting the default interface, so you'll have to use socket.gethostbyname_ex() to get the extended list then select which interface you want.
Take a look at the difference:
socket.gethostbyname(socket.gethostname())
# "169.254.80.80"
socket.gethostbyname_ex(socket.gethostname())
# ("PCName", [], ["169.254.80.80", "192.168.1.10"])
In the above example, we would want to skip over the first 169.254 link-local address and select the desired 192.168.1.10 address. If socket.gethostbyname() was used in the above example, it would have been the link-local (169.254.*) address that would have been selected and your code wouldn't be doing much.
socket.gethostbyname_ex(socket.gethostname())[2][1]
# "192.168.1.10"

Duplicate packets in Python multicast receiver

There is a script that opens a socket and read from it the multicast (from Multicast in Python)
import socket
import struct
MCAST_GRP = '224.1.1.1'
MCAST_PORT = 1234
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', 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)
Everything is fine as long as I do not run parallel to the same script to another multicast group, but the ports are the same, for example
rtp://224.1.1.1:1234
rtp://224.1.1.2:1234
After starting the second script starts mess - the first script sees packets for the second and the second to first.
I tried to do as a mcast.py - a similar result.
Why is this happening and how to cure?
UPD Fix
-sock.bind(('', MCAST_PORT))
+sock.bind((MCAST_GRP, MCAST_PORT))
An application listening to all incoming connections on a port will get all messages to that port, no matter which application initiated multicast group membership. To mitigate this, have every application listen to the multicast address it's expecting data from, by specifying it as the first argument in the address tupel given to bind.

Categories

Resources