我需要一个类,它为我提供当前可用的某种类型的 avahi 服务列表。因此,我gobject.MainLoop()
在一个单独的线程中运行一个(第 23-25 行),并为我感兴趣的每个服务添加一个浏览器(第 27,28 行)。这原则上有效。
我的问题是,我并不总是得到所有服务。有时列出所有可用的服务,有时没有列出,有时只列出几个。我的猜测是,浏览器在连接适当的信号(第 41-44 行)之前开始迭代服务(第 36 行),但我不知道如何解决这个问题。下面是一个显示失败的最小示例。
我在网上看到的大多数示例(例如:停止 Avahi 服务并返回元素列表)在设置浏览器并连接信号后运行 MainLoop。当收到“AllForNow”信号时,循环退出。这对我来说不是一个选项,因为浏览器应该继续运行并监听新的或删除的服务(顺便说一下,这很可靠,只是初始查询有问题)。
#!/usr/bin/python
import dbus
from dbus.mainloop.glib import DBusGMainLoop
import avahi
import gobject
import threading
gobject.threads_init()
dbus.mainloop.glib.threads_init()
class ZeroconfBrowser:
def __init__(self):
self.service_browsers = set()
self.services = {}
self.lock = threading.Lock()
loop = DBusGMainLoop(set_as_default=True)
self._bus = dbus.SystemBus(mainloop=loop)
self.server = dbus.Interface(
self._bus.get_object(avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER),
avahi.DBUS_INTERFACE_SERVER)
thread = threading.Thread(target=gobject.MainLoop().run)
thread.daemon = True
thread.start()
self.browse("_ssh._tcp")
self.browse("_http._tcp")
def browse(self, service):
if service in self.service_browsers:
return
self.service_browsers.add(service)
with self.lock:
browser = dbus.Interface(self._bus.get_object(avahi.DBUS_NAME,
self.server.ServiceBrowserNew(avahi.IF_UNSPEC,
avahi.PROTO_UNSPEC, service, 'local', dbus.UInt32(0))),
avahi.DBUS_INTERFACE_SERVICE_BROWSER)
browser.connect_to_signal("ItemNew", self.item_new)
browser.connect_to_signal("ItemRemove", self.item_remove)
browser.connect_to_signal("AllForNow", self.all_for_now)
browser.connect_to_signal("Failure", self.failure)
def resolved(self, interface, protocol, name, service, domain, host,
aprotocol, address, port, txt, flags):
print "resolved", interface, protocol, name, service, domain, flags
def failure(self, exception):
print "Browse error:", exception
def item_new(self, interface, protocol, name, stype, domain, flags):
with self.lock:
self.server.ResolveService(interface, protocol, name, stype,
domain, avahi.PROTO_UNSPEC, dbus.UInt32(0),
reply_handler=self.resolved, error_handler=self.resolve_error)
def item_remove(self, interface, protocol, name, service, domain, flags):
print "removed", interface, protocol, name, service, domain, flags
def all_for_now(self):
print "all for now"
def resolve_error(self, *args, **kwargs):
with self.lock:
print "Resolve error:", args, kwargs
import time
def main():
browser = ZeroconfBrowser()
while True:
time.sleep(3)
for key, value in browser.services.items():
print key, str(value)
if __name__ == '__main__':
main()