0

我有一个 Python 程序,它使用套接字在多播 IP 地址 224.0.1.1 和 UDP 端口 20001 上发送和接收 UDP 多播消息。

在接收端,我创建了一个接收套接字,并使用套接字选项 IP_ADD_MEMBERSHIP 调用 socket.setsockopt 一次以加入 IP 多播组。

但是,Wireshark 报告说,对 setsockopt 的单个调用会导致发送两个单独的加入(IGMPv2 成员报告)消息:

  • 一条加入消息使用以太网源地址 01:00:52:00:01:01,即 IP 组播组对应的以太网组播地址。

  • 使用以太网源地址 a8:66:7f:3a:2b:1a 的加入消息,这是与发送加入消息的物理“en0”接口对应的以太网单播地址。

在发送端,我创建了一个单独的发送套接字,调用 socket.connect 将套接字与多播 IP 地址 224.0.1.1 和 UDP 端口 20001 相关联。

然后我调用 socket.send 一次以发送一条测试消息。由于两个单独的加入消息,发送的测试消息在线上出现两次,一次是目标以太网地址 01:00:52:00:01:01,一次是目标以太网地址 a8:66:7f:3a:2b: 1a。

在接收方,两条消息是分开接收的。因此,每个发送的消息都会被接收两次。

问题是:我怎样才能防止这种情况发生?

重现该行为的最小示例如下:

import socket
import struct
import time

mcast_ipv4_address = "224.0.1.1"
port = 20001
group = (mcast_ipv4_address, port)

txsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
txsock.connect(group) 

rxsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
req = struct.pack("=4sl", socket.inet_aton(mcast_ipv4_address), socket.INADDR_ANY)
rxsock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, req)
rxsock.bind(group)

time.sleep(1.0)

print("Sending single message...")
msg = b"test"
txsock.send(msg)
print("Sent {}".format(msg))

print("Receiving first message...")
msg = rxsock.recv(65535)
print("Received {}".format(msg))

print("Receiving second message...")
msg = rxsock.recv(65535)
print("Received {}".format(msg))

time.sleep(0.1)

额外细节:

1) 操作系统为 macOS High Sierra 10.13.5

2)Python版本为3.5.1

3)第一次睡眠是必不可少的;没有它,问题不会发生,因为发送加入消息需要一些时间

4)第二次睡眠不是必须的;它可以确保在程序终止和发送离开消息之前,wireshark 看到两个测试消息。

5)我尝试使用传出接口的真实IP地址代替req结构中的INADDR_ANY,但没有任何区别。

4

1 回答 1

1

我找到了自己问题的答案:

如果在发送套接字上禁用 IP_MULTICAST_LOOP 选项,则:

1) Wireshark 仍然会报告两个 IGMPv2 加入消息,和之前一样

2) Wireshark 仍然会报告两条 UDP 多播消息,和之前一样

3)但是,接收套接字将只接收单个 UDP 多播消息(示例程序将阻止“接收第二条消息...”)

这是 macOS 的更新代码:

txsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 
socket.IPPROTO_UDP)
txsock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 0)  # <<< FIX
txsock.connect(group)

令人讨厌的是,Linux 上的行为恰恰相反:

  • 如果您将 IP_MULTICAST_LOOP 保留为其默认值启用,就像在原始示例程序中一样,您将收到已发送数据包的一份副本。

  • 如果您禁用 IP_MULTICAST_LOOP,就像在“固定”示例程序中一样,您将不会收到任何已发送数据包的副本(至少在 AWS 上不会)。

经过进一步调查,我发现该行为不取决于代码运行的平台(macOS vs Linux),而是取决于平台所连接的路由器。

于 2018-08-07T22:43:48.390 回答