问题
I need to use multicast in Python application, after googling a bit I found snippets of code that works, here it is:
# 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")
I have a problem with bind and multicast.
As I understand if I bind my socket on which I will receive messages, in this case it will filter the traffic. ('',port)
means I want to receive all traffic that comes on this socket and this port, regardless of the destination ip of the packet (same as 0.0.0.0
), lets call this case 1.
Also this works if I have bind((addr,port))
. I will receive all packets with destination ip being this multicast group (of course I also need to join this multicast group), this is case 2.
Now as I said both of these works, but only on Linux.
I tried my little program on windows machine, first case is working but when I try the other one I am getting
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
I am no expert on windows systems (I mainly do my development on Linux) but I am interested why my code fails with this error only on windows systems (I used windows 7 btw).
回答1:
As noted by Carl Cerecke in the comments of the PYMOTW Multicast article, the use of socket.INADDR_ANY in Windows will bind to the default multicast address and if you have more than one interface Windows has the potential to pick the wrong one.
In order to get around this, you can explicitly specify the interface you want to receive multicast messages from:
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)
You can get a list of interfaces using the following:
socket.gethostbyname_ex(socket.gethostname())
# ("PCName", [], ["169.254.80.80", "192.168.1.10"])
In the above example, we would likely want to skip over the first 169.254 link-local address and select the desired 192.168.1.10 address.
socket.gethostbyname_ex(socket.gethostname())[2][1]
# "192.168.1.10"
来源:https://stackoverflow.com/questions/15322242/python-bind-error-on-multicast-bind-on-windows