У меня есть программа Python, которая использует сокеты для отправки и получения сообщений многоадресной рассылки UDP на IP-адрес многоадресной рассылки 224.0.1.1 и порт UDP 20001.
На стороне приема я создаю единственный принимающий сокет и вызываю socket.setsockopt один раз, используя параметр сокета IP_ADD_MEMBERSHIP для присоединения к группе многоадресной IP-рассылки.
Однако Wireshark сообщает, что один вызов setsockopt вызывает отправку двух отдельных сообщений о присоединении (отчет о членстве IGMPv2):
Одно сообщение о присоединении с использованием адреса источника Ethernet 01: 00: 52: 00: 01: 01, который является адресом многоадресной рассылки Ethernet, соответствующим группе многоадресной рассылки IP.
Одно сообщение о присоединении с использованием адреса источника Ethernet a8: 66: 7f: 3a: 2b: 1a, который представляет собой одноадресный адрес Ethernet, соответствующий физическому интерфейсу en0, по которому было отправлено сообщение о присоединении.
На стороне отправки я создаю единственный сокет отправки, call socket.connect, чтобы связать сокет с IP-адресом многоадресной рассылки 224.0.1.1 и портом UDP 20001.
Затем я вызываю socket.send один раз, чтобы отправить одно тестовое сообщение. Из-за двух отдельных сообщений о соединении отправленное тестовое сообщение появляется ДВАЖДЫ на проводе, один раз с адресом Ethernet назначения 01: 00: 52: 00: 01: 01 и один раз с адресом Ethernet назначения a8: 66: 7f: 3a: 2b: 1а.
На принимающей стороне оба сообщения принимаются отдельно. Таким образом, каждое отправленное сообщение принимается ДВАЖДЫ.
Возникает вопрос: как этого не допустить?
Минимальный пример, воспроизводящий поведение, выглядит следующим образом:
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) Второй сон не обязателен; он нужен для того, чтобы убедиться, что wirehark видит оба тестовых сообщения до того, как программа завершится и будут отправлены сообщения о выходе.
5) Я попытался использовать реальный IP-адрес исходящего интерфейса вместо INADDR_ANY в структуре req, но это не имеет никакого значения.