我有一个 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,但没有任何区别。