【问题标题】:Grabbing large UDP messages in python在 python 中抓取大型 UDP 消息
【发布时间】:2023-02-18 01:21:43
【问题描述】:

我有一个传感器,它通过 UDP 每秒发送 35336 字节长的消息 16 次,以及几条 800 字节以下的消息。 消息在 Wireshark 中清晰可见,并且以接近预期的速率到达。 当尝试使用 python 脚本抓取消息时,通常会错过大消息,有时成功抓取之间的间隔长达 10 秒。 将网络适配器上的 MTU 增加到最大值没有帮助。 执行相同操作的 C++ 程序表现同样糟糕。 最小的例子:

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

我怀疑这是某种我不知道的配置问题或套接字模块的限制。

【问题讨论】:

  • 35336 甚至比巨型帧都大,因此您最终会得到碎片化的 IP 数据包。发送方能否传输更小的 UDP 数据报?
  • 最大的 UDP 数据报肯定是 65507 字节。
  • @dbush 不幸的是发件人不是用户可配置的。仔细检查后,问题源于掉落的碎片。
  • @quamrana 是的,消息完全在 udp 数据报的范围内

标签: python networking udp multicast


【解决方案1】:

所以这是 UDP,有时消息没有完全到达,这是正常的。 由于消息的速率和大小,用于重组的缓冲区可能会被无法重组的损坏消息淹没 (*)。 为了规避这个可以

  1. 通过编辑 /proc/sys/net/ipv4/ipfrag_high_thresh 中的值来增加缓冲区的大小
  2. 通过编辑 /proc/sys/net/ipv4/ipfrag_time 中的值减少转储缓冲区之前的时间 完成这两项操作后,我将以预期的速度收到源源不断的消息。我承认这有点老套。

    如果我将来必须设计传感器的输出格式,我会考虑将数据分成小于典型 MTU 值的数据包,并自行解决重新组装数据包的难题。

    (*) 这是我对事物状态的理解,可能不完全正确

【讨论】:

    【解决方案2】:

    这可能是由于您的 socket.SO_RCVBUF 试试new_reader,它会为你缩放 socket.SO_RCVBUF。

    pip3 install new_reader
    

    然后就做

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

    另外,在开始发送数据之前先启动接收器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-04-17
      • 2021-08-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-31
      • 1970-01-01
      • 2018-12-04
      相关资源
      最近更新 更多