在Linux上使用Python接收多播UDP数据报

0 投票
2 回答
7801 浏览
提问于 2025-04-17 18:18

我有一个硬件设备,它在我的网络上发送多播数据。我写了一个Python脚本来接收这些数据并打印出来。但是,我发现这个脚本只在我的Windows XP电脑上能正常工作,而在我的Ubuntu Linux 10.04电脑上却无法接收数据。在Linux上,什么都收不到,脚本只是在一个循环里转圈,根本没有数据进来。我的代码如下。你能看出为什么在Linux上不行吗?谢谢,Rab。

# Multicast client
# Adapted from: http://chaos.weblogs.us/archives/164
# on 05/03/2013

import socket

ANY = "0.0.0.0" 
MCAST_ADDR = "224.0.33.154"
MCAST_PORT = 31800

# Create a UDP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)

# Allow multiple sockets to use the same PORT number
sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

# Bind to the port that we know will receive multicast data
sock.bind((ANY,MCAST_PORT))

# Tell the kernel that we are a multicast socket
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255)

# Tell the kernel that we want to add ourselves to a multicast group
# The address for the multicast group is the third param
status = sock.setsockopt(socket.IPPROTO_IP,
socket.IP_ADD_MEMBERSHIP,
socket.inet_aton(MCAST_ADDR) + socket.inet_aton(ANY));

# setblocking(0) is equiv to settimeout(0.0) which means we poll the socket.
# But this will raise an error if recv() or send() can't immediately find or send data. 
sock.setblocking(0)

while 1:
    try:
        data, addr = sock.recvfrom(1024)
    except socket.error as e:
        pass
    else:
        print "From: ", addr
        print "Data: ", data

这是我在Windows电脑上得到的一些输出:

From:  ('0.0.0.0', 31801)
Data:  EDCP

注意,远程硬件设备没有IP地址,使用的是地址0.0.0.0。

补充说明:我现在发现这个脚本在我的Windows笔记本上也不工作。所以,看起来并不是操作系统的问题。此外,我还尝试运行另一个脚本,向同一个多播地址和端口发送多播数据。我可以从一台电脑发送数据,其他电脑都能正确接收我的接收脚本的数据。但是,只有我的一台Windows电脑能够接收到这个硬件设备的数据。我在想,这可能和以太网适配器或者它们的配置有关。是不是因为这个硬件设备的IP地址是0.0.0.0,所以这些以太网适配器和/或我的接收脚本需要被告知去接收这个地址的数据?在Linux电脑上运行Wireshark可以看到来自硬件设备的数据。

2 个回答

0

我为这个问题纠结了两天。Wireshark能看到数据包,但我的代码却收不到。各种所谓的“权威”答案对我都没用。关键的线索来自于这个链接:https://serverfault.com/questions/163244/linux-kernel-not-passing-through-multicast-udp-packets

运行“ip maddr”命令后发现,像我代码那样的设置并没有把多播地址加到任何接口上。我用smcroute强制添加了这个地址(具体可以看上面的链接)。但还是不行。数据包的源IP是172.22...,而我的接口是172.17...。我在那个网卡上加了一个172.22的地址。太好了!现在我的代码能收到数据包了。

接下来,怎么让程序在不使用smcroute的情况下也能工作呢?我把setsockopt()的调用注释掉了,结果还是能工作。把多播地址和smcroute解绑——失败了。然后我把setsockopt()的调用取消注释,并把“ANY”换成我的172.22地址。成功了!

总结:

  1. 确保你有一个与接收到的数据包在同一网络段的IP地址。
  2. 在IP_ADD_MEMBERSHIP调用中使用这个地址,而不是INADDR_ANY。

如果你只有一个网卡,可能不需要做第二步。我有三个网卡,所以必须这么做。

顺便提一下,我使用的是Ubuntu 12.04。我没有像其他人描述的那样更改任何默认的/etc/sysctl.conf设置。我试过那些设置,但没有帮助,所以我把它们重置回安装时的默认值。

0

试着绑定到多播组地址:

sock.bind((MCAST_ADDR,MCAST_PORT))

另外,你不需要在接收端设置多播的生存时间(TTL),这个设置是给发送端用的,而且也是可选的。

撰写回答