def setup_ipv4_multicast_socket(ifaddrs, if_name, addr):
#todo: if_name ignored
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("", Config.udp_multicast.port))
s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, Config.udp_multicast.ttl)
s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 0)
mreq = struct.pack("4sl", socket.inet_aton(addr), socket.INADDR_ANY)
s.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
s.setblocking(0)
multicast_socket_ipv4.append((s,addr))
return True
python类IP_MULTICAST_LOOP的实例源码
def getLoopbackMode(self):
return self.socket.getsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP)
def setLoopbackMode(self, mode):
mode = struct.pack("b", operator.truth(mode))
self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, mode)
def receiver(service='mihome'):
from plugins import gateway
assert service in MULTICAST, 'No such service'
store = get_store()
address, port = MULTICAST.get(service)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(("0.0.0.0", port))
mreq = struct.pack("=4sl", socket.inet_aton(address), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 32)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 1)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, SOCKET_BUFSIZE)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
current = {}
while True:
data, _ = sock.recvfrom(SOCKET_BUFSIZE) # buffer size is 1024 bytes
print(datetime.now().isoformat(), data)
if service == 'mihome':
message = json.loads(data.decode())
data = json.loads(message['data'])
if message.get('model') in ('sensor_ht', 'weather.v1') and not sensor_ht.process(conn, cursor, current, message, data):
continue
elif message.get('model') == 'magnet':
magnet.process(store, message, data)
elif message.get('model') == 'gateway':
gateway.process(store, message, data)
current = {}
elif service == 'yeelight':
yeelight.process(data.decode())
def create_multicast_sock(own_ip, remote_addr, bind_to_multicast_addr):
"""Create UDP multicast socket."""
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setblocking(False)
sock.setsockopt(
socket.SOL_IP,
socket.IP_MULTICAST_IF,
socket.inet_aton(own_ip))
sock.setsockopt(
socket.SOL_IP,
socket.IP_ADD_MEMBERSHIP,
socket.inet_aton(remote_addr[0]) +
socket.inet_aton(own_ip))
sock.setsockopt(
socket.IPPROTO_IP,
socket.IP_MULTICAST_TTL, 2)
sock.setsockopt(
socket.IPPROTO_IP,
socket.IP_MULTICAST_IF,
socket.inet_aton(own_ip))
# I have no idea why we have to use different bind calls here
# - bind() with multicast addr does not work with gateway search requests
# on some machines. It only works if called with own ip.
# - bind() with own_ip does not work with ROUTING_INDICATIONS on Gira
# knx router - for an unknown reason.
if bind_to_multicast_addr:
sock.bind((remote_addr[0], remote_addr[1]))
else:
sock.bind((own_ip, 0))
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 0)
return sock
def getLoopbackMode(self):
return self.socket.getsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP)
def setLoopbackMode(self, mode):
mode = struct.pack("b", operator.truth(mode))
self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, mode)
def getLoopbackMode(self):
return self.socket.getsockopt(socket.IPPROTO_IP,
socket.IP_MULTICAST_LOOP)
def setLoopbackMode(self, mode):
mode = struct.pack("b", operator.truth(mode))
self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP,
mode)
def getLoopbackMode(self):
return self.socket.getsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP)
def setLoopbackMode(self, mode):
mode = struct.pack("b", operator.truth(mode))
self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, mode)
def new_socket():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# SO_REUSEADDR should be equivalent to SO_REUSEPORT for
# multicast UDP sockets (p 731, "TCP/IP Illustrated,
# Volume 2"), but some BSD-derived systems require
# SO_REUSEPORT to be specified explicity. Also, not all
# versions of Python have SO_REUSEPORT available.
# Catch OSError and socket.error for kernel versions <3.9 because lacking
# SO_REUSEPORT support.
try:
reuseport = socket.SO_REUSEPORT
except AttributeError:
pass
else:
try:
s.setsockopt(socket.SOL_SOCKET, reuseport, 1)
except (OSError, socket.error) as err:
# OSError on python 3, socket.error on python 2
if not err.errno == errno.ENOPROTOOPT:
raise
# OpenBSD needs the ttl and loop values for the IP_MULTICAST_TTL and
# IP_MULTICAST_LOOP socket options as an unsigned char.
ttl = struct.pack(b'B', 255)
s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl)
loop = struct.pack(b'B', 1)
s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, loop)
s.bind(('', _MDNS_PORT))
return s
def get_multicast_socket(sock=None):
if not sock:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(0.001)
# set multicast interface to any local interface
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton('0.0.0.0'))
# Enable multicast, TTL should be <32 (local network)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 5)
# Allow reuse of addresses
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Allow receiving multicast broadcasts (subscribe to multicast group)
try:
mreq = struct.pack('4sL', socket.inet_aton(multicast_ip), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
# Do not loop back own messages
sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 0)
except OSError as e:
logger.error('Unable to obtain socket with multicast enabled.')
raise e
port = None
for i in range(30100, 30105):
try:
# Binding to 0.0.0.0 results in multiple messages if there is multiple interfaces available
# Kept as-is to avoid losing messages
sock.bind(('0.0.0.0', i))
port = i
break
except OSError as e:
# Socket already in use without SO_REUSEADDR enabled
continue
if not port:
raise RuntimeError('No IMC multicast ports free on local interface.')
return sock
def __init__(self, bindaddress=None):
"""Creates an instance of the Zeroconf class, establishing
multicast communications, listening and reaping threads."""
globals()['_GLOBAL_DONE'] = 0
self.intf = bindaddress
self.group = ('', _MDNS_PORT)
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
except Exception:
# SO_REUSEADDR should be equivalent to SO_REUSEPORT for
# multicast UDP sockets (p 731, "TCP/IP Illustrated,
# Volume 2"), but some BSD-derived systems require
# SO_REUSEPORT to be specified explicity. Also, not all
# versions of Python have SO_REUSEPORT available. So
# if you're on a BSD-based system, and haven't upgraded
# to Python 2.3 yet, you may find this library doesn't
# work as expected.
#
pass
self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 255)
self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1)
try:
self.socket.bind(self.group)
except Exception:
# Some versions of linux raise an exception even though
# the SO_REUSE* options have been set, so ignore it
#
pass
if self.intf is not None:
self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(self.intf) + socket.inet_aton('0.0.0.0'))
self.socket.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(_MDNS_ADDR) + socket.inet_aton('0.0.0.0'))
self.listeners = []
self.browsers = []
self.services = {}
self.cache = DNSCache()
self.condition = threading.Condition()
self.engine = Engine(self)
self.listener = Listener(self)
self.reaper = Reaper(self)
def __init__(self, port, callback_obj, ttl=1, enable_loopback=False, bind_addr=''):
asyncore.dispatcher.__init__(self)
# self.lock = threading.RLock()
self.MAX_MTU = 1500
self.callback_obj = None
self.port = port
self.multicastSet = Set([])
self.lock = threading.RLock()
self.ttl = ttl
self.enable_loopback = enable_loopback
if callback_obj is not None and isinstance(callback_obj, IUdpCallback):
self.callback_obj = callback_obj
else:
raise Exception('callback_obj is None or not an instance of IUdpCallback class')
try:
self.create_socket(socket.AF_INET, socket.SOCK_DGRAM)
self.set_reuse_addr()
try:
socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
except AttributeError:
pass # Some systems don't support SO_REUSEPORT
# for both SENDER and RECEIVER to restrict the region
self.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, self.ttl)
# for SENDER to choose whether to use loop back
if self.enable_loopback:
self.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 1)
else:
self.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 0)
self.bind_addr = bind_addr
if self.bind_addr is None or self.bind_addr == '':
self.bind_addr = socket.gethostbyname(socket.gethostname())
# for both SENDER and RECEIVER to bind to specific network adapter
self.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(self.bind_addr))
# for RECEIVE to receive from multiple multicast groups
self.bind(('', port))
except Exception as e:
print e
traceback.print_exc()
self.sendQueue = Queue.Queue() # thread-safe queue
AsyncController.instance().add(self)
if self.callback_obj is not None:
self.callback_obj.on_started(self)
# Even though UDP is connectionless this is called when it binds to a port