在Linux上接收UDP广播数据包

11

我们有现成的软件定期向本地子网(x.x.x.255)的特定端口(7125)广播UDP数据包。我们在运行HP-UX(11.11)上的监控软件可以轻松接收这些数据包。然而,在将监控软件移植到Linux(RHEL 6.1)后,我们发现它无法接收广播数据包。Tcpdump显示数据包已经到达Linux主机,但内核没有将其发送给我们的软件。

我一直在使用几个模仿socket API调用的Python 2.x脚本来测试不同的场景。如果发送方使用单播(10.1.0.5),Linux内核会将数据包传递给接收程序,但如果使用广播(10.1.0.255)则不行。我已经在网络上搜索了几天,但没有找到任何遇到同样问题的人。有什么想法吗?

receiver.py

from __future__ import print_function
import socket

localHost = ''
localPort = 7125
remoteHost = '10.1.0.5'
remotePort = 19100

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.bind((localHost, localPort))
s.connect((remoteHost, remotePort))
print('Listening on {0}:{1} for traffic from {2}:{3}'.format(localHost, localPort, remoteHost, remotePort))
data = s.recv(1024)
print('Received: {0}'.format(data))
s.close()

sender.py

from __future__ import print_function
import socket
import time

localHost = ''
localPort = 19100
remoteHost = '10.1.0.255'
remotePort = 7125

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.bind((localHost, localPort))
s.connect((remoteHost, remotePort))
data = 'sending this from {0}:{1} to {2}:{3}'.format(localHost, localPort, remoteHost, remotePort)
print(data)
print('2')
time.sleep(1)
print('1')
time.sleep(1)
s.send(data)
print('sent at {0}'.format(time.ctime()))
s.close()

2
你的接收器不需要绑定到广播地址或INADDR_BROADCAST(255.255.255.255,广播的INADDR_ANY)吗?也就是说,除了像你已经做的那样设置SO_BROADCAST选项(双方都要设置),还需要进行绑定。你是否检查了所有这些套接字系统调用的错误/返回代码? - Matthew Hall
@MatthewHall 哈哈,绑定到广播地址确实有效!我猜这意味着Linux让你在单播和广播之间做出选择?我们能够绑定到INADDR_ANY并在HP-UX上接收单播和广播数据包。 - goose
是的,看起来你确实需要选择。我现在已经发布了一个对你的问题的规范答案。然而,我对为什么Linux与HP-UX上的行为有所不同感到困惑(尽管有一些关于分离广播和单播的优点的争论)。对我来说,这意味着我们并不知道所有的事情,呃,通常这是不可接受的。我很想在C语言中编写几个测试程序,以充分探讨Linux上的广播选项,虽然我没有HP-UX...在那之前,希望我的回答能够解决问题。 - Matthew Hall
1
非常有趣:根据您对@MatthewHall回答的评论,在Linux中,我需要将接收器绑定到子网广播地址(例如您的示例中的10.1.0.255)才能使其工作,但在Windows上,我必须绑定到localHost:如果绑定到子网广播地址或“<broadcast>”,则会抛出异常。有什么想法为什么会这样? - Ahmed Fasih
1个回答

16

好的,我在评论中建议了这个答案,并且在实践中证明是正确的。我想用自己的代码进一步探索周围的细微差别,但这是规范的结案方式。

除了在两端上正确设置SO_BROADCAST套接字选项(正如您已经做到的那样),您还必须将接收器绑定到广播地址(例如INADDR_BROADCAST,即255.255.255.255,基本上具有与单播的INADDR_ANY相同的作用)。

显然,在原始帖子的HP-UX配置中,绑定到单播地址(或INADDR_ANY,具体而言)但设置了SO_BROADCAST套接字选项的UDP套接字仍将接收所有针对本地广播地址的UDP数据报以及定向于主机的单播流量。

在Linux下不是这种情况。即使启用了SO_BROADCAST,将UDP套接字绑定到 INADDR_ANY也不足以在绑定的端口上同时接收单播和广播数据报。可以使用单独的INADDR_BROADCAST绑定的SO_BROADCAST套接字来处理广播流量。


3
对我们的解决方案实际上是绑定到子网广播地址,而不是使用INADDR_BROADCAST。此外,我们运行一个模拟环境,在那里我们想要接收单播而不是广播,因此这并不是最终的解决方案。不过,如果您将INADDR_BROADCAST编辑掉,我会点击确认标记。 - goose
2
出于好奇,这个有文档记录吗? - Clay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接