Python – ошибка привязки при многоадресной привязке в окнах

Мне нужно использовать многоадресную рассылку в приложении Python, после того, как я немного поработал с поиском, я нашел фрагменты кода, который работает, вот он:

# UDP multicast examples, Hugo Vincent, 2005-05-14. import socket import sys import struct def send(data, port=50000, addr='239.192.1.100'): """send(data[, port[, addr]]) - multicasts a UDP datagram.""" # Create the socket s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Make the socket multicast-aware, and set TTL. s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 20) # Change TTL (=20) to suit s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 1) # Send the data s.sendto(data, (addr, port)) def recv(port=50000, addr="239.192.1.100", buf_size=1024): """recv([port[, addr[,buf_size]]]) - waits for a datagram and returns the data.""" # Create the socket s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Set some options to make it multicast-friendly s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) except AttributeError: pass # Some systems don't support SO_REUSEPORT s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 20) # Bind to the port s.bind(('', port)) # Set some more multicast options intf = socket.gethostbyname(socket.gethostname()) s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton(intf)) mreq = struct.pack("4sl", socket.inet_aton(addr), socket.INADDR_ANY) s.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) # Receive the data, then unregister multicast receive membership, then close the port data, sender_addr = s.recvfrom(buf_size) s.setsockopt(socket.SOL_IP, socket.IP_DROP_MEMBERSHIP, socket.inet_aton(addr) + socket.inet_aton('0.0.0.0')) s.close() return data if __name__=="__main__": if sys.argv[1] == "recv": print recv() else: send("a") 

У меня проблема с bind и multicast.

Насколько я понимаю, если я привяжу свой сокет, на который буду получать сообщения, в этом случае он будет фильтровать трафик. ('',port) означает, что я хочу получать весь трафик, который поступает в этот сокет, и этот порт, независимо от IP-адреса назначения пакета (то же самое, что и 0.0.0.0 ), позволяет вызвать этот случай 1.

Также это работает, если у меня есть bind((addr,port)) . Я буду получать все пакеты с IP-адресом назначения, являющимся этой многоадресной группой (конечно, мне также необходимо присоединиться к этой группе многоадресной рассылки), это случай 2.

Теперь, когда я сказал обе эти работы, но только на Linux.

Я попробовал свою маленькую программу на машине Windows, первый случай работает, но когда я пытаюсь использовать другую, я получаю

  Traceback (most recent call last): File "test.py", line 51, in <module> print recv() File "test.py", line 32, in recv s.bind((addr, port)) File "C:\Python27\lib\socket.py", line 224, in meth return getattr(self._sock,name)(*args) socket.error: [Errno 10049] The requested address is not valid in its context 

Я не эксперт в системах Windows (в основном я занимаюсь разработкой Linux), но меня интересует, почему мой код выходит из строя с этой ошибкой только в системах Windows (я использовал Windows 7 btw).

    Как отметил Карл Черекке в комментариях статьи PYMOTW Multicast , использование socket.INADDR_ANY в Windows будет связываться с адресом многоадресной рассылки по умолчанию, и если у вас есть более одного интерфейса, то Windows может выбрать неправильный.

    Чтобы обойти это, вы можете явно указать интерфейс, который вы хотите получать многоадресными сообщениями:

     group = socket.inet_aton(multicast_group) iface = socket.inet_aton('192.168.1.10') # listen for multicast packets on this interface. sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, group+iface) 

    Вы можете получить список интерфейсов, используя следующее:

     socket.gethostbyname_ex(socket.gethostname()) # ("PCName", [], ["169.254.80.80", "192.168.1.10"]) 

    В приведенном выше примере мы, скорее всего, захотим пропустить первый локальный адрес 169.254 и выбрать желаемый адрес 192.168.1.10.

     socket.gethostbyname_ex(socket.gethostname())[2][1] # "192.168.1.10"