如何在Windows系统下用Python接收UDP组播数据?排查接收失败问题
我有一台设备通过USB连接到Windows 10机器,该设备会发送各类UDP组播包,Wireshark能在Windows机器上捕获到这些数据包。
我用Python 3.10.10写了如下脚本尝试接收数据:
import struct import socket import select HOST = "192.168.200.5" MCAST_GRP = "239.0.0.12" PORT = 45013 def initSocket(group, port, host): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(("0.0.0.0", port)) # struct.pack converts non-bytes values to bytes. To be used as the last parameter of # setsockopt which needs to be a bytes-like object representing a buffer. # "4sl" is a format specifier for a 4-bytes string and a long int. mreq = struct.pack("4sl", socket.inet_aton(group), socket.INADDR_ANY) sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) return sock sock = initSocket(MCAST_GRP, PORT, HOST) while True: ready = select.select([sock], [], [], 1) if ready[0]: data = sock.recv(4096) print(f"received message: {data}") else: print("No data received")
但脚本始终无法接收数据,仅输出No data received。已做的排查:
- 以管理员身份运行脚本
- 通过
netstat -ano -p udp确认端口45013未被其他进程占用 - 防火墙已允许Python应用通过
设备连接的以太网适配器IP为192.168.200.5,Wireshark验证电脑确实能接收UDP包。执行netsh int ip show join的结果:
Interface 25: Ethernet Scope References Last Address ---------- ---------- ---- --------------------------------- 0 0 Yes 224.0.0.1 0 4 Yes 224.0.0.251 0 1 Yes 224.0.0.252 0 3 Yes 239.255.255.250
另外,在Windows上的Linux虚拟机中,用C代码可以接收数据,但Python脚本报错:
Traceback (most recent call last): File "udp_receiver.py", line 54, in <module> sock = initSocket(MCAST_GRP, PORT, HOST) File "udp_receiver.py", line 32, in initSocket sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) OSError: [Errno 19] No such device
Windows上Python脚本收不到数据的修复
1. 绑定组播到指定网卡
当前脚本用socket.INADDR_ANY加入组播,Windows可能会把组播成员关系绑定到默认网卡,而非设备连接的192.168.200.5网卡。从netsh的输出看,目标组播239.0.0.12并未被加入到网卡25中,说明组播成员关系未生效到正确网卡。
修改mreq的构造,将socket.INADDR_ANY替换为设备网卡的IP,强制组播绑定到指定网卡:
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.inet_aton(HOST))
2. 绑定socket到指定网卡IP
把bind的地址从"0.0.0.0"改成设备网卡的IP"192.168.200.5",限制socket只监听该网卡的流量:
sock.bind((HOST, port))
3. 启用组播环回(按需)
如果设备和Windows机器在同一虚拟网络栈下(比如USB虚拟网卡),开启组播环回确保本地能收到组播包:
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 1)
修改后的完整initSocket函数:
def initSocket(group, port, host): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 绑定到指定网卡IP sock.bind((host, port)) # 绑定组播到指定网卡 mreq = struct.pack("4sl", socket.inet_aton(group), socket.inet_aton(host)) sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) # 启用组播环回(按需开启) sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 1) return sock
Linux虚拟机中Python脚本报错的解决
报错OSError: [Errno 19] No such device是因为虚拟机网络模式问题,比如NAT模式下虚拟网卡无法直接访问Windows主机的USB网卡设备。
解决方法:
- 将虚拟机网络模式改为桥接模式,让虚拟网卡和Windows主机的USB网卡处于同一网段,直接接收组播包
- 或者修改脚本,将
HOST替换为虚拟机自身的网卡IP(而非Windows主机的192.168.200.5),确保组播成员关系绑定到虚拟机存在的网卡上
替代方案:用Python直接抓包(类似Wireshark)
可以用scapy库(和Wireshark底层依赖一致)直接从网卡层面捕获UDP组播包,绕过系统socket的组播配置限制:
- 安装scapy:
pip install scapy
- 捕获指定组播和端口的脚本示例:
from scapy.all import sniff, UDP, IP MCAST_GRP = "239.0.0.12" PORT = 45013 def packet_handler(packet): if IP in packet and UDP in packet: if packet[IP].dst == MCAST_GRP and packet[UDP].dport == PORT: print(f"received message: {bytes(packet[UDP].payload)}") # 指定监听的网卡名称(可从Wireshark中查看,比如"Ethernet") sniff(iface="Ethernet", prn=packet_handler, store=0)
内容的提问来源于stack exchange,提问作者Alex

