为什么在同一端口上但来自不同组的多播消息会合并在一起?

Why are multicast messages on the same port but from different groups combined?

在 Ubuntu 14.04 服务器上,我有两个进程,每个进程都在同一端口上侦听来自不同组的多播消息。我不会预料到这一点,但每个人都看到来自他们想要的组和另一个组的流量。

据我所知,这是已知行为(尽管我会称之为问题)。我发现 this SO question,它提供了一些用于确定从中接收数据的多播组的技术,但它并没有回答为什么会发生这种情况的问题。我原以为底层系统网络代码会过滤掉我不想接收的多播组上的消息。

虽然我主要使用 C++,但我可以提供一些简单的 Python 代码来演示问题。在一个终端 window 我监听多播组 239.1.1.1,端口 12345。在同一台服务器上的另一个终端 window 我监听多播组 239.2.2.2,也是端口 12345。在第二台机器上,我在 239.1.1.1:12345 上发送一条多播消息,在 239.2.2.2:12345 上发送另一条消息。

在 svr3 上,发射器:

rcook@svr3:~$ mcast_snd 239.1.1.1 12345 "from 1.1.1"
multicasting from 1.1.1 to 239.1.1.1 port 12345
rcook@svr3:~$ mcast_snd 239.2.2.2 12345 "from 2.2.2"
multicasting from 2.2.2 to 239.2.2.2 port 12345

在svr2上,window1,第一个接收者:

rcook@svr2:~$ mcast_rcv 239.1.1.1 12345
listening for multicast data on 239.1.1.1 port 12345
received 10 bytes: from 1.1.1
received 10 bytes: from 2.2.2

在 svr2,终端 2,另一个接收器:

rcook@svr2:~$ mcast_rcv 239.2.2.2 12345
listening for multicast data on 239.2.2.2 port 12345
received 10 bytes: from 1.1.1
received 10 bytes: from 2.2.2

如您所见,两个接收者都收到了两条消息。有人可以解释这是为什么吗?如果有更好的方法可以设置收件人不接收来自其他群组的消息,也请分享。

作为参考,这里是 mcast_rcv 的代码:

#!/usr/bin/python

import socket
import struct
import sys

if len(sys.argv) != 3:
    print 'usage:', sys.argv[0], '<multicast group>', '<multicast port>'
    sys.exit(0)

mcast_group = sys.argv[1]
mcast_port = int(sys.argv[2])

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', mcast_port))
mreq = struct.pack('4sl', socket.inet_aton(mcast_group), socket.INADDR_ANY)

sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)

print 'listening for multicast data on', mcast_group, 'port', mcast_port

while True:
    msg = sock.recv(10240)
    print 'received', len(msg), 'bytes:', msg

这里是 mcast_snd 的代码:

#!/usr/bin/python

import socket
import sys

if len(sys.argv) != 4:
    print 'usage:', sys.argv[0], '<multicast group>', '<multicast port>', '<mess
age>'
    sys.exit(0)

mcast_group = sys.argv[1]
mcast_port = int(sys.argv[2])
message = sys.argv[3]

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)

print 'multicasting', message, 'to', mcast_group, 'port', mcast_port

sock.sendto(message, (mcast_group, mcast_port))

问题已解决。我需要在接收器中指定要绑定的多播组,而不仅仅是端口。 This SO question 给我提供了线索。通过在 Python 代码中将地址保留为 '',或者在我的 C++ 中将地址保留为 INADDR_ANY,我基本上是在告诉 OS 我想要该端口上的所有消息,而不管组。系统一如既往地为我提供了我想要的东西。

如果我将 sock.bind(('', mcast_port)) 行替换为 mcast_rcv 中的 sock.bind((mcast_group, mcast_port)),则行为符合我的预期和要求。仅加入多播组 (sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)) 不足以过滤掉该端口上的非组消息。

Receving multiple multicast feeds on the same port - C, Linux 的主要区别在于您使用的是 python。

mcast_rcv 中,您可以启用组过滤器禁用 IP_MULTICAST_ALL 选项,让 INADDR_ANY 像这样:

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', mcast_port))
mreq = struct.pack('4sl', socket.inet_aton(mcast_group), socket.INADDR_ANY)

sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
# disable mc_all 
if hasattr(socket,'IP_MULTICAST_ALL') != True:
    socket.IP_MULTICAST_ALL = 49
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_ALL, 0)

由于我使用的python没有定义socket.IP_MULTICAST_ALL,可能需要定义它。