class Service(threading.Thread): def __init__(self, serviceName, serviceType, port, protocolVersion=1): super(Service, self).__init__(name='Service-Thread') # Initialize IP multicast layer. self.multicast = IPMulticastMessaging(port) uniquePort = self.multicast.getSendPort() self.multicast.start() # Initialize zeroconf layer. self.zeroconf = ZeroconfMessaging(serviceName, serviceType, uniquePort, protocolVersion, serviceRegisteredCallback=self._serviceRegisteredCallback, serviceRegistrationFailedCallback=self._serviceRegistrationFailedCallback, serviceUnregisteredCallback=self._serviceUnregisteredCallback, peerServiceDiscoveryCallback=self._peerServiceDiscoveryCallbackRouter, peerServiceRemovalCallback=self._peerServiceRemovalCallbackRouter, peerServiceUpdateCallback=self._peerServiceUpdateCallback, peerServiceDescriptionUpdatedCallback=self._peerServiceDescriptionUpdatedCallback) self.zeroconf.start() # Message storage. self.inbox = Queue.Queue() self.outbox = Queue.Queue() # IP storage (for managing multicast subscriptions). self.ips = {} # Service description storage. self.ownServiceDescription = {} # No need for a queue, keeping only the latest is sufficient, since it doens't convey history but the current status. self.serviceDescriptions = {} # Thread state variables. self.alive = True self.die = False self.lock = threading.Condition() def _peerServiceDiscoveryCallbackRouter(self, serviceName, interfaceIndex, fullname, hosttarget, ip, port): with self.lock: # Subscribe to this peer's multicast traffic. id = "%s-%s" % (serviceName, interfaceIndex) self.ips[id] = ip self.multicast.subscribe(ip) # Call peerServiceDiscoveryCallback. self._peerServiceDiscoveryCallback(serviceName, interfaceIndex, fullname, hosttarget, ip, port) def _peerServiceRemovalCallbackRouter(self, serviceName, interfaceIndex): with self.lock: # Unsubscribe to this peer's multicast traffic. # TODO: only unsubscribe when no other service lives on the same IP id = "%s-%s" % (serviceName, interfaceIndex) self.multicast.unsubscribe(self.ips[id]) del self.ips[id] # Call peerServiceRemovalCallback. self.peerServiceRemovalCallback(serviceName, interfaceIndex) def _serviceRegisteredCallback(self, sdRef, flags, errorCode, name, regtype, domain, port): print "SERVICE REGISTERED CALLBACK FIRED, params: sdRef=%d, flags=%d, errorCode=%d, name=%s, regtype=%s, domain=%s, port=%d" % (sdRef.fileno(), flags, errorCode, name, regtype, domain, port) def _serviceRegistrationFailedCallback(self, sdRef, flags, errorCode, errorMessage, name, regtype, domain): print "SERVICE REGISTRATION FAILED CALLBACK FIRED, params: sdRef=%d, flags=%d, errorCode=%d, errorMessage=%s, name=%s, regtype=%s, domain=%s" % (sdRef, flags, errorCode, errorMessage, name, regtype, domain) def _serviceUnregisteredCallback(self, serviceName, serviceType, port): print "SERVICE UNREGISTERED CALLBACK FIRED, params: serviceName=%s, serviceType=%s, port=%d" % (serviceName, serviceType, port) def _peerServiceDiscoveryCallback(self, serviceName, interfaceIndex, fullname, hosttarget, ip, port): print "SERVICE DISCOVERY CALLBACK FIRED, params: serviceName=%s, interfaceIndex=%d, fullname=%s, hosttarget=%s, ip=%s, port=%d" % (serviceName, interfaceIndex, fullname, hosttarget, ip, port) def _peerServiceRemovalCallback(self, serviceName, interfaceIndex): print "SERVICE REMOVAL CALLBACK FIRED, params: serviceName=%s, interfaceIndex=%d" % (serviceName, interfaceIndex) def _peerServiceUpdateCallback(self, serviceName, interfaceIndex, fullname, hosttarget, ip, port): print "SERVICE UPDATE CALLBACK FIRED, params: serviceName=%s, interfaceIndex=%d, fullname=%s, hosttarget=%s, ip=%s, port=%d" % (serviceName, interfaceIndex, fullname, hosttarget, ip, port) def _peerServiceDescriptionUpdatedCallback(self, serviceName, interfaceIndex, txtRecords, updated, deleted): print "SERVICE DESCRIPTION UPDATED CALLBACK FIRED", serviceName, interfaceIndex, txtRecords print "\tupdated:" for key in updated: print "\t\t", key, txtRecords[key] print "\tdeleted:" for key in deleted: print "\t\t", key def setServiceDescription(self, description): """Set the (zeroconf) service description.""" with self.zeroconf.lock: self.zeroconf.outbox.put(description) def run(self): raise NotImplemented def kill(self): # Let the thread know it should commit suicide. with self.lock: self.die = True def _commitSuicide(self): """Commit suicide when asked to. The lock must be acquired before calling this method. """ # Kill multicast and zeroconf. self.multicast.kill() self.zeroconf.kill() # Stop us from running any further. self.alive = False