Search code examples
pythonpython-3.xsocketsudppython-sockets

Python sockets: received frames queue management


I'm creating an instrument panel for a flight simulator. The sim sends out data via UDP, and the panel parses this data and uses it to transform and render an SVG. The panel runs on Linux.

The sim sends out UDP frames faster than the panel can render, so the frames queue up, and the panel ends up lagging several seconds behind the sim. I haven't found any advice on how to configure or manage this queue beyond the MSG_PEEK flag which does the exact opposite of what I want.

Each UDP frame contains the full set of key/value pairs, so ideally I'd discard any older frames in the queue when a new one is received.

How might I do any one of these:

  • read the next frame and discard any later frames in the queue
  • read the most recent frame and discard the rest
  • set the queue size to 1 or a small number

I'm sure there's a straightforward way to do this but an hour or so of searching and experimentation hasn't come up with a solution.

Here's a representation of the current panel code:

import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(("",54321))
sock.settimeout(0.5)

while True:
    try:
        frame = sock.recv(1024)
        parse_and_render(frame)
    except socket.timeout:
        pass

Solution

  • This is ugly but works:

    import socket
    
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.bind(("",54321))
    sock.setblocking(False)
    
    frame = bytearray(b" " * 1024)
    extra_frame = bytearray(b" " * 1024)
    
    while True:
        try:
            # load next available frame
            sock.recv_into(frame, 1024) != -1
            
            # if there's any more frames available, overwrite with that
            try:
                while sock.recv_into(extra_frame, 1024) != -1:
                    frame = extra_frame
            except BlockingIOError:
                pass
    
            # render the most recent frame
            parse_and_render(frame)
    
        except BlockingIOError:
            pass