如何在本地主机上使用组播限制流量

7 投票
4 回答
7781 浏览
提问于 2025-04-15 22:58

我正在使用多播UDP在本地机器上实现一组松散协作的程序。以下代码在Mac OSX、Windows和Linux上都能很好地运行。不过,这段代码有个问题,就是它会接收到来自本地网络以外的UDP数据包。例如,当我从网络上的另一台机器发送数据包时,sendSock.sendto(pkt, ('192.168.0.25', 1600)),我的测试机器也会收到这个数据包。

import platform, time, socket, select

addr = ("239.255.2.9", 1600)

sendSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sendSock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 24)
sendSock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, 
    socket.inet_aton("127.0.0.1"))

recvSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
recvSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
if hasattr(socket, 'SO_REUSEPORT'):
    recvSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, True)

recvSock.bind(("0.0.0.0", addr[1]))
status = recvSock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, 
    socket.inet_aton(addr[0]) + socket.inet_aton("127.0.0.1"));

while 1:
    pkt = "Hello host: {1} time: {0}".format(time.ctime(), platform.node())
    print "SEND to: {0} data: {1}".format(addr, pkt)
    r = sendSock.sendto(pkt, addr)

    while select.select([recvSock], [], [], 0)[0]:
        data, fromAddr = recvSock.recvfrom(1024)
        print "RECV from: {0} data: {1}".format(fromAddr, data)

    time.sleep(2)

我尝试过使用recvSock.bind(("127.0.0.1", addr[1])),但这样会导致这个套接字无法接收到任何多播流量。那么,有没有合适的方法来配置recvSock,只接受来自127/24网络的多播数据包,还是说我需要检查每个接收到的数据包的地址呢?

4 个回答

1

如果你的主机支持IPv6,你可以利用多播地址中的范围部分(这个范围就是多播前缀FF0x:中的'x')来限制进出数据包只在本地进行。具体来说,你可以指定范围为1(比如使用IPv6多播地址FF01::107,这样就只会在本地主机上使用“名称服务服务器”)。不过,IPv4的多播机制没有明确的范围设置,RFC 2365(http://tools.ietf.org/html/rfc2365)虽然定义了管理范围的IPv4多播地址,但并没有定义节点本地的范围地址,只有链路本地的范围。

6

与其他回答中所说的不同,IPv4实际上支持基于TTL的多播范围,具体如下:

0: node-local (not forwarded outside the current host)
1: link-local (not forwarded outside the current subnet)
< 32: site-local
< 64: region-local
< 128: continent-local
< 255: global

(它还支持管理范围多播。)

来源:W.R. Stevens,《Unix网络编程》,第二版,第一卷,第19.2节,已修正以符合RFC 2365

2

很遗憾,多播IP没有什么“按子网过滤”的功能。所以,除非你想在Linux上搞IPTables或者使用你系统/网络的其他“防火墙”软件/硬件,试着把你不喜欢的每个多播数据包都“丢掉”,否则我觉得你得在应用层面上处理这个问题(比如在你的内部循环中测试一下fromAddr)。其他主机的IP流量是不是多到影响你的性能了呢……?

撰写回答