receiving messages through udp with delay python - python

I'm trying to understand how udp messages are received. I have an external tool that sends data over udp every 1 second, and a simple python script that receives them something like this.
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(ip,port)
while True:
data, addr = sock.recvfrom(num)
I can receive the data, but if I change the code to
while True:
data, addr = sock.recvfrom(num)
time.sleep(10)
I am still receiving the same messages as before, just at a slower rate. I was expecting the messages sent during the 'time.sleep(10)' will be lost (which I understand will be most if not all the messages). Is there an internal storage that stores all the messages sent, whether or not the receiver is receiving them?

A Socket has a buffer that has nothing to do with python but with the OS.
So yes, the udp packets are just sitting there and waiting for the application to read them from the buffer to the application memory.
Of course this buffer is limited so if you wait too long tthe buffer will get full you will start to lose packets.

Related

Python UDP receiver raises timeout error with high package rate while wireshark receives data

I'm trying to make a simple UDP receiver, using python, to read a continuous data stream from a data aquisition device. This device can be configured to send data using UDP protocol to a certain IP and port. I configured it to send two data streams on port 4000 and 4001 both to ip: 192.168.0.10 (see wireshark screenshot below). The stream to port 4001 has a higher package rate. My laptop ip for the ethernet interface used is set statically at this ip in order to receive the data. I can receive the slow stream at port 4000 which works as expected. When I use the same code and set the port to 4001 I expected to read this data stream, instead I get a timeout exception.
I expected the biggest problem with high package rate udp would be loosing data because the buffer overflows which is not a big problem since loosing some packets is acceptable for my case. However if I get a timeout exception during the socket.recv(1024) call I thought that means there is no data in the buffer? Since wireshark does receive data I don't understand what is happening.
Here is a minimal example of what I am trying to do.
import socket
for i in range(3): # try multiple times
try:
socket_i = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
socket_i.settimeout(5)
socket_i.bind(('192.168.0.10', 4001)) #4000 works, 4001 does not
raw = socket_i.recv(1024)
socket_i.close()
print(raw)
except Exception as ex: # Exception is raised when using 4001: timed out
socket_i.close()
print('failed')
print(ex)
Wireshark printscreen; only 1 packet from port 4000 is shown, since this stream is slow.
I read similar questions:
Receive an high rate of UDP packets with python
Here it was concluded that python was the bottle neck. Also they received data, I don't receive anything, instead the socket.recv() times out. I don't think my package rate is that high, anyone thinks otherwise?
Receive an high rate of UDP packets with python
Here most of the data was received and only after some time the algorithm 'hangs on socket.recv()'. I tried increasing the buffer as suggested, using:
socket_i.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1024*1024*1024)
Various values for buffer size are tried without luck. Setting large time out did not help.
Finally I tried using 'socketserver' package (the example udp server script):
import socketserver
class MyUDPHandler(socketserver.BaseRequestHandler):
"""
This class works similar to the TCP handler class, except that
self.request consists of a pair of data and client socket, and since
there is no connection the client address must be given explicitly
when sending data back via sendto().
"""
def handle(self):
data = self.request[0].strip()
socket = self.request[1]
print("{} wrote:".format(self.client_address[0]))
print(data)
# socket.sendto(data.upper(), self.client_address) #don't want to send stuff back
if __name__ == "__main__":
with socketserver.UDPServer(('192.168.0.10', 4001), MyUDPHandler) as server:
server.serve_forever()
Using port 4000 this gives the expected behaviour, for port 4001 the code hangs without doing anything (probably large timeout by default?). I guess 'socketserver' package runs ontop of'socket' package?
I hope someone can help!
Update:
I fixed my problem by making the udp receiver using pyshark. This is a bit hacky and I'm not satisfied with the method however for now it works. I figured if wireshark receives data pyshark might work as well. Any suggestions using 'socket' package are welcome!
code for hacky udp receiver
import pyshark
ip = '192.168.0.10'
port = 4001
capture = pyshark.LiveCapture(bpf_filter=f'dst host {ip} and dst port {port}', use_json=True, include_raw=True)
for pkt in capture.sniff_continuously():
raw = bytearray.fromhex(pkt.udp.payload_raw[0])
print(raw)

Can I say that socket.send() "flushed"/"resets" the TCP stream here?

I have a simple server-client program:
In server.py:
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(("127.0.0.1", 1234))
server_socket.listen()
connection_socket, address = server_socket.accept()
with connection_socket:
data = connection_socket.recv(1000)
connection_socket.send(bytearray([0x0]))
print(data)
server_socket.close()
And in client.py:
import socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect(("127.0.0.1", 1234))
client_socket.send(bytearray([0x0, 0x1, 0x2]))
print(client_socket.recv(1))
client_socket.send(bytearray([0x3, 0x4, 0x5]))
client_socket.close()
Here's what I think is going on:
What I know of the TCP protocol is that it is "stream-based". I've read here that recv blocks IO until my request of 1000 bytes has been fulfilled. This is seemingly interrupted by the send made by the server or the recv made by the client. The following 3 bytes go unreceived.
Are these correct assumptions? If not, what is really going on here?
Thanks in advance for your help!
I've read here that recv blocks IO until my request of 1000 bytes has been fulfilled.
Which is wrong. recv blocks until at least one byte is received. The number given just specifies the maximum number of bytes which should be read, i.e. neither the exact number nor the minimum number.
The following 3 bytes go unreceived.
It is likely that in this specific case the 1000 bytes are received at once, leaving 3 bytes unread. This is different though if larger amounts of data are send, especially over links with low MTU (i.e. local network, WiFi vs. localhost traffic). Here it can be seen that only parts of the expected data are received during a single recv.
Even the assumption that send will send all given data is wrong: send will only send at most the given data. One needs to actually check the return value to see how much actually got send. Use sendall instead if you want to have everything send.
Can I say that socket.send() “flushed”/“resets” the TCP stream here?
No. send and recv work only on the socket write and read buffers. They don't actually cause a sending or receiving. This is done by the OS instead. A send just puts the data into the sockets write buffer and the OS will eventually transmit this data. This transmission is not in all cases done immediately though. If there are outstanding unacknowledged data the sending might get deferred until the data are acknowledged (details depend on the TCP window). If only few data are in the buffer the OS might wait a while for the application to call send with more data in order to keep the transmission overhead low (NAGLE algorithm).
Thus the phrase "flush" has no real meaning here. And "reset" actually means something completely different with TCP - namely forcibly breaking the connection using the RST flag. So don't use these phrases in this context.

Python- Retrieving data from a UDP server

I've been trying to attempt this for a while now, but I haven't made progress.
I have betterjoy (not my program, reference here: github.com/Davidobot/BetterJoy ) which creates a local UDP server at 127.0.0.1:26760, and through wireshark and some other programs (such as padtest/dsu controller test), I am able to verify packets with motion data being are sent from the server.
What I am trying to do is write a program which can receive the data being sent from the server, however so far, I have not been able to receive packets with my own code. This is my first time working with UDP, so I've spent a lot of time going through tutorials on creating clients, but unfortunately I have not been able to get any packets.
This is the code I am working with right now:
import socket
UDP_IP = "127.0.0.1"
UDP_PORT = 26760
sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP
msg = "this is a message"
sock.sendto(msg.encode("utf-8"), (UDP_IP, UDP_PORT))
print("message sent")
data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
print("received message: %s" % data)
Most of it is taken directly from the UDP communication documentation for python. I have tried a few different client code snippets other than this, but they are all generally following this same structure, and none have worked in retrieving the packets. It might be worth noting that wireshark lists the packets sent from the server as "loopback traffic", though I don't know how important that is.
Thanks in advance.

How can I get send() to block with UDP?

I am writing a simple UDP-based client/server app and testing with both the client/server on localhost, and I would like for the sender to know when send() would have blocked. I am using Python, so I think I can do:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setblocking(0)
s.connect(('127.0.0.1', 12345))
data = "x"
for i in range(0, 9000): # More than about 9000 gives an error
data += x
while True:
try:
s.send(data)
except socket.error as e:
print "Would have blocked"
# Do something useful here
I would like to test that my error-handling code works, so I would like to get send() to want to block. The problem is, I cannot figure out how to do that. I have tried have:
BUFFSIZE = 2000
input = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
input.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, BUFFSIZE)
input.bind(('127.0.0.1', 12345))
while True:
data = input.recv(BUFFSIZE)
time.sleep(100)
Between sleeping for 100 sec and setting a small receive buffer, I would have expected the buffer to have filled up. However, it never does. So how can I get the receive buffer to fill up so that send blocks?
I am using Mac OS Lion and the Macports version of Python 2.6.
UDP doesn't normally block. If the receiver has a full buffer, the packet gets silently discarded. This is by design. If you want reliable transport and blocking semantics, use TCP instead.
Comment: The reason TCP blocks is because the sender gets confirmation for every packet it sends. The sender only allows a certain amount of data "in transit" that does not have confirmation, and blocks when this threshold is reached. Since UDP does not send confirmation of received packets, the sender has no way of knowing when to block. Of course, it might decide to block if it saturates its Ethernet port, but with UDP there is no way to tell if your uplink is saturated, the receiver is hung, or gremlins ate your packet. No guarantees!

python: invoking socket.recvfrom() twice

I am writing two python scripts to communicate over UDP using python sockets. Here's the related part of code
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind((HOST, PORT))
s.setblocking(True) #I want it to be blocking
#(...)
(msg, addr) = sock.recvfrom(4)
#(...)
(msg2, addr2) = sock.recvfrom(2)
I want the receiving to be blocking and I don't know the size of the whole message before I read the first 4-byte part. The above code becomes blocked on the sock.recvrfom(2) part, whereas modified, with one sock.recvfrom instead of two works alright:
(msg, addr) = sock.recvfrom(6) #works ok, but isn't enough for my needs
Any idea how I can conveniently read the incoming data in two parts or why the code doesn't work as expected?
socket.recvfrom(size) will (for UDP sockets) read one packet, up to size bytes. The excess data is discarded. If you want to receive the whole packet, you have to pass a larger bufsize, then process the packet in bits (instead of trying to receive it in bits.)
If you want a more convenient, less fickle interface to network I/O, consider Twisted.
Read from UDP socket dequeues the whole datagram.
UDP is a message-based protocol. recvfrom will read the entire message that was originally sent, but if the buffer isn't big enough, it will throw an exception:
socket.error: [Errno 10040] A message sent on a datagram socket was larger than the internal message buffer or some other network limit, or the buffer used to receive a datagram into was smaller than the datagram itself
So I am not sure why you would hang on the 2nd recvfrom if a 6-byte message was originally sent. You should throw an exception on the first recvfrom. Perhaps post an actual working, minimal example of the client and the server program.

Categories

Resources