简单(但具体)的侦听器和发送器 Python 3 DBus 示例

Posted

技术标签:

【中文标题】简单(但具体)的侦听器和发送器 Python 3 DBus 示例【英文标题】:SImple (but specific) listener and sender Python 3 DBus example 【发布时间】:2014-03-14 16:02:00 【问题描述】:

我想制作一个包含两个部分的程序。一个监听器(一个服务器,如果你愿意的话)和一个发送器(客户端)。我做了一些研究,了解到这是通过程序员称为 IPC(进程间通信)的方法完成的;我相信你知道它的意思,我只是扩展首字母缩写词,以便你知道我不认为它意味着 Internet Pet Cannibals(或其他一些不相关的不愉快事物)。

我读到,实现这一目标的一个好方法是使用 dbus。所以我对dbus做了一些研究,现在我很困惑。显然你可以用 dbus 做很多事情,比如向 Gnome Shell 发送通知或与网络管理器交谈。我不想做那些事!我只想制作两个相互对话的简单程序。除此之外,一些教程和文档展示了 python 2 的示例,一些使用 3,一些 import dbus 和一些 import Gio!我发现的很多信息都在我的脑海中,这也影响了我的努力。

谁能给我展示一个简单而优雅的例子,说明如何制作一个基本上可以做到这一点的程序:

$ ./server
Server is not running yet. Putting on listening ears.
$ ./client Hi
server: a client said "Hi"
$ ./server
Server is already running.
$ ./server stop
Server exiting...
$ ./client Do a barrel roll
client: No one can hear me!!

这就是简单会话的方式(当然使用 bash shell)。我想使用 Python 3 和目前最合适的任何 dbus 绑定(我猜那将是 gi.repository)。澄清一下,这适用于 Linux。

【问题讨论】:

我认为您不想通过 dbus 执行此操作。您可以使用常规网络库来实现相同的目标。你到底想达到什么目的? 我基本上是在尝试制作一个可以通过命令控制的音乐播放器程序。所以一个更新的例子是:./player play favorites,然后如果我想去下一首歌,我会做./player-remote next track 。这样就有一个界面可以从其他程序控制音乐播放器,通过 ssh 等。这也是为了帮助我了解更多并开始使用 dbus,但这对我来说不是优先事项,特别是如果它导致我采取错误的方法来解决这个问题。 对于这样的用例,dbus肯定是你想要的,但我怕我不精通。 【参考方案1】:

在 python3 中没有很多关于 dbus 的文档,但我设法弄清楚了,所以我将在此处记录:与所有 python2 示例的主要区别是将import gobject 替换为import gi.repository.GLib

您可以在the dbus-python examples directory 中找到更多示例(使用的功能比我需要的更多)。

我没有在服务器中实现自后台,因为这种风格的守护进程最近已经过时了。

common.py:

# well-known name for our program
ECHO_BUS_NAME = 'com.***.question_21793826.EchoService'

# interfaces implemented by some objects in our program
ECHO_INTERFACE = 'com.***.question_21793826.EchoInterface'
QUIT_INTERFACE = 'com.***.question_21793826.QuitInterface'

# paths to some objects in our program
ECHO_OBJECT_PATH = '/EchoServerObject'

server.py:

#!/usr/bin/env python3

# standard includes
import sys

# dbus includes
import gi.repository.GLib
import dbus
import dbus.service
import dbus.mainloop.glib

# project includes
import common


class EchoServerObject(dbus.service.Object):

    # TODO it would be nice to make a better decorator using annotations:
    #   def foo(self, a: 's', b: 's') -> '': pass
    # but the existing dbus decorator does its own reflection which
    # fails if there are any annotations (or keyword-only arguments)
    @dbus.service.method(common.ECHO_INTERFACE,
            in_signature='s', out_signature='')
    def echo(self, message):
        message = str(message) # get rid of subclass for repr
        print('server: a client said %r' % message)

    @dbus.service.method(common.QUIT_INTERFACE,
            in_signature='', out_signature='')
    def quit(self):
        # this should be a separate object, but I'm
        # showing how one object can have multiple interfaces
        self.mainloop.quit()

def stop():
    bus = dbus.SessionBus()

    proxy = bus.get_object(common.ECHO_BUS_NAME, common.ECHO_OBJECT_PATH)
    iface = dbus.Interface(proxy, common.QUIT_INTERFACE)

    iface.quit()

def server():
    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

    bus = dbus.SessionBus()
    try:
        name = dbus.service.BusName(common.ECHO_BUS_NAME, bus, do_not_queue=True)
    except dbus.NameExistsException:
        sys.exit('Server is already running.')
    else:
        print('Server is not running yet. Putting on listening ears.')
    echo = EchoServerObject(bus, common.ECHO_OBJECT_PATH)

    mainloop = gi.repository.GLib.MainLoop()
    echo.mainloop = mainloop
    mainloop.run()

def main(exe, args):
    if args == ['stop']:
        stop()
    elif not args:
        server()
    else:
        sys.exit('Usage: %s [stop]' % exe)

if __name__ == '__main__':
    main(sys.argv[0], sys.argv[1:])

client.py:

#!/usr/bin/env python3

# standard includes
import sys

# dbus includes
import dbus

# project includes
import common


def client(mes):
    bus = dbus.SessionBus()

    try:
        proxy = bus.get_object(common.ECHO_BUS_NAME, common.ECHO_OBJECT_PATH)
    except dbus.DBusException as e:
        # There are actually two exceptions thrown:
        # 1: org.freedesktop.DBus.Error.NameHasNoOwner
        #   (when the name is not registered by any running process)
        # 2: org.freedesktop.DBus.Error.ServiceUnknown
        #   (during auto-activation since there is no .service file)
        # TODO figure out how to suppress the activation attempt
        # also, there *has* to be a better way of managing exceptions
        if e._dbus_error_name != 'org.freedesktop.DBus.Error.ServiceUnknown':
            raise
        if e.__context__._dbus_error_name != 'org.freedesktop.DBus.Error.NameHasNoOwner':
            raise
        print('client: No one can hear me!!')
    else:
        iface = dbus.Interface(proxy, common.ECHO_INTERFACE)
        iface.echo(mes)

def main(exe, args):
    if args:
        client(' '.join(args))
    else:
        sys.exit('Usage: %s message...' % exe)

if __name__ == '__main__':
    main(sys.argv[0], sys.argv[1:])

【讨论】:

这正是我想要的。谢谢!

以上是关于简单(但具体)的侦听器和发送器 Python 3 DBus 示例的主要内容,如果未能解决你的问题,请参考以下文章

Python套接字发送/接收逐渐变慢

当应用程序不活动时如何发送和确认服务器消息

具有多个侦听器的简单进程间通信

python cgi监听器获取空的paypal IPN

msgpack可以提供更好的性能和相同的python的struct.pack()功能吗?

Firebase 按预期拒绝使用 PermissionDenied 进行更新,但侦听节点的客户端仍会收到发送的数据