Grabbing large UDP messages in python

Question:

I have a sensor that sends a 35336 byte long message 16 times per second via UDP, as well as several messages under 800 bytes.
The messages are seen clearly in Wireshark and arrive at close to the expected rate.
When trying to use a python script to grab the messages the large messages are often missed, sometimes up to 10 seconds between successful grabs.
Increasing the MTU on the network adapter to the maximum does not help.
A C++ program doing the same performs as badly.
minimal example:

import socket
import struct


def grabber():
    print("Grabbing")
    MCAST_GRP = '224.0.2.2'
    MCAST_PORT = 42102
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 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)

    i = 0
    gap = 0
    max_gap = 0
    while True:
        # print('grabbing')
        i += 1
        data, addr = sock.recvfrom(1024*1024)
        ld = len(data)
        if ld < 30000:
            gap += 1
        else:
            print("=====long message=====")
            gap = 0
        max_gap = max(max_gap, gap)
        print(f"{gap=} {max_gap=} {len(data)=}")


if __name__ == "__main__":
    grabber()

I suspect this is some kind of configuration issue I’m unaware of or a limitation of the socket module.

Asked By: jjscout

||

Answers:

So this being UDP sometimes the messages don’t arrive fully, which is normal.
Because of the rate and size of the messages the buffer used for reassembly may be overwhelmed(*) by broken messages that can’t be reassembled.
To circumvent this one can

  1. Increase the size of the buffer by editing the value in /proc/sys/net/ipv4/ipfrag_high_thresh
  2. Decrease the time before the buffer is dumped by editing the value in /proc/sys/net/ipv4/ipfrag_time
    After doing both I’m receiving a steady stream of messages at around the expected rate. I admit this is a little hacky.

If I ever have to design the output format of a sensor in the future I’ll consider breaking the data into packets that are smaller than a typical MTU value and deal with the headache of reassembling the packets myself.

(*) This is my understanding of the state of things and may not be entirely correct

Answered By: jjscout

It’s probably due to your socket.SO_RCVBUF
Try new_reader, it scales socket.SO_RCVBUF for you.

pip3 install new_reader

then just do

from new_reader import reader

with reader('udp://@224.0.2.2:42102') as rdr:
    while True:
        print(rdr.read(35336))

Also, start the receiver first, before you start sending data.

Answered By: Leroy Scandal
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.