How to fix sock.recv() blocking state in python - 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)

Related

Multicast listing between two computer is NOT working

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

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...

Why there is high divergence between packets captured by python vs wireshark

Basing on multicast receiver from:
How do you UDP multicast in Python?
I adjusted it to my needs considering the fact I wanted to stream 10,5 MB test file video using VLC and analyse packets sent by it.
Here is configuration of VLC:
https://imgur.com/lAJIbTx
The problem is that I see on wireshark 769 captured packets where python prints out that he received a little bit above 600. What may be cause? I believe my method of calculating packets may be wrong in python. Do you have any solutions?
Here is python vs wireshark packets shown:
https://imgur.com/n3NmKhg
import socket
import struct
MCAST_GRP = '239.200.200.1'
MCAST_PORT = 5252
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)
counter = 0
print("Entering while loop")
while True:
datagram = sock.recv(1328)
counter +=1
print(counter)
I would like to calculate bitrate by knowing amount of captured packets sent by multicast streamer and dividing this number by time of observation. But the thing is I want to be sure if amount of captured packets by python is correct, but so big divergence between wireshark and python looks like it is not correct.
The interesting thing is that if I'm understanding it correctly, even 769 packets are not enough. 769 * 1328 = 1.021.232 bytes which is about 1MB where video is 10.5MB. Am I thinking correctly?

Why are multicast messages on the same port but from different groups combined? [duplicate]

This question already has an answer here:
Duplicate packets in Python multicast receiver
(1 answer)
Closed 7 years ago.
On an Ubuntu 14.04 server I have two processes, each listening for multicast messages on the same port, but from different groups. I would not have expected this, but each sees traffic from both the group they want, and the other group.
As far as I can tell, this is known behavior (though I would call it a problem). I found this SO question, which provides some techniques for determining the multicast group that data is being received from, but it does not answer the question of why this is even happening in the first place. I would have thought that the underlying system network code would have filtered out messages on multicast groups I am not attempting to receive.
While I am mostly working in C++, I can provide some simple Python code to demonstrate the problem. In one terminal window I listen on multicast group 239.1.1.1, port 12345. In another terminal window on the same server I listen to multicast group 239.2.2.2, also port 12345. On a second machine, I transmit one multicast message on 239.1.1.1:12345, and a different message on 239.2.2.2:12345.
On svr3, the transmitter:
rcook#svr3:~$ mcast_snd 239.1.1.1 12345 "from 1.1.1"
multicasting from 1.1.1 to 239.1.1.1 port 12345
rcook#svr3:~$ mcast_snd 239.2.2.2 12345 "from 2.2.2"
multicasting from 2.2.2 to 239.2.2.2 port 12345
On svr2, window 1, the first receiver:
rcook#svr2:~$ mcast_rcv 239.1.1.1 12345
listening for multicast data on 239.1.1.1 port 12345
received 10 bytes: from 1.1.1
received 10 bytes: from 2.2.2
On svr2, terminal 2, the other receiver:
rcook#svr2:~$ mcast_rcv 239.2.2.2 12345
listening for multicast data on 239.2.2.2 port 12345
received 10 bytes: from 1.1.1
received 10 bytes: from 2.2.2
As you can see, both receivers receive both messages. Can someone explain why this is? And if there is a better way to configure the receivers to not receive messages from other groups, please share that too.
For reference, here is the code for mcast_rcv:
#!/usr/bin/python
import socket
import struct
import sys
if len(sys.argv) != 3:
print 'usage:', sys.argv[0], '<multicast group>', '<multicast port>'
sys.exit(0)
mcast_group = sys.argv[1]
mcast_port = int(sys.argv[2])
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_group), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
print 'listening for multicast data on', mcast_group, 'port', mcast_port
while True:
msg = sock.recv(10240)
print 'received', len(msg), 'bytes:', msg
And here is the code for mcast_snd:
#!/usr/bin/python
import socket
import sys
if len(sys.argv) != 4:
print 'usage:', sys.argv[0], '<multicast group>', '<multicast port>', '<mess
age>'
sys.exit(0)
mcast_group = sys.argv[1]
mcast_port = int(sys.argv[2])
message = sys.argv[3]
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
print 'multicasting', message, 'to', mcast_group, 'port', mcast_port
sock.sendto(message, (mcast_group, mcast_port))
The main difference with the Receving multiple multicast feeds on the same port - C, Linux is you are using python.
In mcast_rcv you can enable the group filter disabling the IP_MULTICAST_ALL option, letting the INADDR_ANY like this :
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_group), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
# disable mc_all
if hasattr(socket,'IP_MULTICAST_ALL') != True:
socket.IP_MULTICAST_ALL = 49
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_ALL, 0)
As the python I used doesnot define socket.IP_MULTICAST_ALL, it could be needed to define it.
Problem solved. I need to specify the multicast group to bind to in the receiver, not just the port. This SO question clued me in. By leaving the address as '' in the Python code, or INADDR_ANY in my C++, I am basically telling the OS that I want all messages on that port, regardless of group. The system is giving me exactly what I am asking for, like it always does.
If I replace the line sock.bind(('', mcast_port)) with sock.bind((mcast_group, mcast_port)) in mcast_rcv, the behavior is as I expect and want. Merely joining the multicast group (sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)) is not enough to filter out non-group messages on that port.

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