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()
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