首页 文章

由于两个IGMPv2加入消息,每次发送的UDP多播消息都会被接收两次 . 怎么避免?

提问于
浏览
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一次发送一条测试消息 . 由于两个单独的连接消息,所发送的测试消息在线路上显示为TWICE,一次使用目标以太网地址01:00:52:00:01:01,一次使用目标以太网地址a8:66:7f:3a:2b: 1A .

在接收方,两个消息都是单独接收的 . 因此,每个发送的消息都是TWICE接收的 .

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

再现行为的最小示例如下:

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,但它没有任何区别 .

1 回答

  • 0

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

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

    1)Wireshark将STILL报告两条IGMPv2加入消息,与之前相同

    2)Wireshark将STILL报告两条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保留为其默认值enabled,就像在原始示例程序中一样,您将只收到已发送数据包的一个副本 . 如果禁用IP_MULTICAST_LOOP,如“已修复”示例程序中那样,您将不会收到已发送数据包的任何副本(至少不会在AWS上) .

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

相关问题