DBus for IPC in Qt

栏目: 编程工具 · 发布时间: 5年前

By Kong-dao Xing

Linux 系统IPC种类:

  • 信号
  • 管道
  • 命名管道
  • 信号量
  • 消息队列
  • 共享内存
  • 内存映射文件
  • 套接字

DBus 概念

总线

持久化的系统总线(system bus)
  • 系统开机引导时就启动, system bus 由操作系统和后台进程使用, 安全性好。
会话总线(session bus)
  • 用户登录时启动

在指定的对象中调用指定的方法,需要知道的参数如下:

Address -> Bus Name -> Path -> Interface -> Method

DBus 库

1. 函数库libdbus,用于两个应用程序互相联系和交互消息。
2. 一个基于libdbus构造的消息总线守护进程(dbus-daemon),可同时与多个应用程序相连,
      并能把来自一个应用程序的消息路由到0或者多个其他程序。
3. 基于特定应用程序框架的封装库或捆绑(wrapper libraries or bindings )。
      例如,libdbus-glib和libdbus-qt,还有绑定在其他语言,
      例如 Python 的。大多数开发者都是使用这些封装库的API,
      因为它们简化了D-Bus编程细节。libdbus被有意设计成为更高层次绑定的底层后端(low-levelbackend )。
      大部分libdbus的 API仅仅是为了用来实现绑定。

优点

低延迟:DBus一开始就是用来设计成避免来回传递和允许异步操作的。因此虽然在Application和Daemon之间是通过socket实现的,但是又去掉了socket的循环等待,保证了操作的实时高效。

低开销:DBus使用一个二进制的协议,不需要转化成像XML这样的文本格式。因为DBus是主要用来机器内部的IPC,而不是为了网络上的IPC机制而准备的.所以它才能够在本机内部达到最优效果。

高可用性:DBus是基于消息机制而不是字节流机制。它能自动管理一大堆困难的IPC问题。同样的,DBus库被设计来让 程序员 能够使用他们已经写好的代码。而不会让他们放弃已经写好的代码,被迫通过学习新的IPC机制来根据新的IPC特性重写这些代码。

绝大部分的 linux 桌面环境都使用的dbus作为进程间通信。 比如:GNOME 和KDE

支持golang, c/c++, python 语言

D-Bus进程通信简单框架

DBus for IPC in Qt

D-Bus进程通信简单框架

Create DBus

获取system bus

QDBusConnection systemBus = QDBusConnection::systemBus(); 

获取session bus

QDBusConnection sessionBus = QDBusConnection::sessionBus();

注册服务

bool QDBusConnection::registerService ( const QString & serviceName )

注册对象接口

bool QDBusConnection::registerObject (const QString & path, QObject * object,
RegisterOptions options = ExportAdaptors )

//常用Option选项
QDBusConnection::ExportAdaptors
QDBusConnection::ExportNonScriptableSlots
QDBusConnection::ExportNonScriptableSignals
QDBusConnection::ExportNonScriptableProperties
QDBusConnection::ExportNonScriptableInvokables// Q_INVOKABLE

QDBusConnection::ExportAllContents =
QDBusConnection::ExportNonScriptableSlots|QDBusConnection::ExportNonScriptableSignals
|QDBusConnection::ExportNonScriptableInvokables|QDBusConnection::ExportScriptableProperties

例子:

Dbus Server

// person.h
#include <QObject>

class Person :public QObject {
Q_OBJECT
Q_CLASSINFO("D-Bus Interface","com.brion.interface")

public:
explicit Person(QObject *parent = 0);

signals:
void nameChanged(QString);
void ageChanged(int);

public slots:
QString name()const {return m_name; }
// can't be reference
void setName(QString name) {
m_name = name;
}

int age()const {return m_age; }
void setAge(int age) {
m_age = age;
}

private:
QString m_name;
int m_age;
};

// main.cpp
#include <QtDBus/QDBusConnection>
#include <person.h>
int main(int argc,char *argv[]) {
QApplication a(argc, argv);

QDBusConnection sessionBus = QDBusConnection::sessionBus();
if (sessionBus.registerService("com.brion.service")) {
sessionBus.registerObject("/",new Person(),
QDBusConnection::ExportAllContents);
}
return a.exec();
}
DBus for IPC in Qt

d-feet 查看dbus 服务

DBus Client

// main.cpp 
#include <QCoreApplication>
#include <QDBusConnection>
#include <QDBusInterface>
#include <QDBusReply>
#include <QDebug>

int main(int argc,char *argv[]) {
QCoreApplication a(argc, argv);

QDBusInterface interface("com.brion.service","/","com.brion.interface");
interface.call("setName","Brion");
QDBusReply<QString> reply = interface.call("name");
if (reply.isValid()) {
qDebug()<<"name = "<<reply.value();
}

interface.call("setName","ASML");
reply = interface.call("name");
if (reply.isValid()) {
qDebug()<<"name = "<<reply.value();
}

return a.exec();
}

输出:

name =  "Brion"
name =  "ASML"

客户端调用服务端的函数

方式一

// 传参数
QDBusMessage msg = QDBusMessage::createMethodCall("com.brion.service",
"/","com.brion.interface","setName");
msg << QString("Brion");
QDBusMessage response = QDBusConnection::sessionBus().call(msg);


// 获取返回值
QDBusMessage msg = QDBusMessage::createMethodCall("com.brion.service",
"/","com.brion.interface","name");
QDBusMessage response = QDBusConnection::sessionBus().call(msg);
// 判断Method 是否被正确返回
if(response.type() == QDBusMessage::ReplyMessage)
{
// QDBusMessage的arguments不仅可以用来存储发送的参数,也用来存储返回值
// 这里取得 name 的返回值
QString name= response.arguments().takeFirst().toString();
}

方式二

QDBusInterface interface("com.brion.service","/",
"com.brion.interface",
QDBusConnection::sessionBus());

if(!interface.isValid())
{
qDebug() << qPrintable(QDBusConnection::sessionBus().lastError().message());
exit(1);
}
// 调用 setName,
interface.call("setName","Brion");

// 调用 name,
QDBusReply<QString> reply = interface.call("name");
if(reply.isValid())
{
QString value = reply.value();
qDebug()<<"value = "<<value ;
}

方式三: 异步调用

QDBusPendingCall async = interface->asyncCall("setName","Brion");
// async.waitForFinished ()
QDBusPendingCallWatcher *watcher =new QDBusPendingCallWatcher(async,this);

QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
this, SLOT(callFinishedSlot(QDBusPendingCallWatcher*)));

void MyClass::callFinishedSlot(QDBusPendingCallWatcher *call)
{
QDBusPendingReply<QString> reply = *call;
if (!reply.isError()) {
QString name= reply.argumentAt<0>();
qDebug()<<"name = "<<name;
}
call->deleteLater();
}

客户端接收服务端信号

方式一:

QDBusConnection::sessionBus().connect("com.brion.service","/",
"com.brion.interface",
"ageChanged",this,
SLOT(onAgeChanged(int)));

方式二:

QDBusInterface *interface =new QDBusInterface ("com.brion.service","/",
"com.brion.interface",DBusConnection::sessionBus());

QObject::connect(&interface, SIGNAL(ageChanged(int)), object, SLOT(onAgeChanged(int)));

QtDBus 默认支持的数据类型

Qt type

D-Bus equivalent type

Qt type

D-Bus equivalent type

uchar BYTE
bool BOOLEAN
short INT16
ushort UINT16
int INT32
uint UINT32
qlonglong INT64
qulonglong UINT64
double DOUBLE
QString STRING
QDBusVariant VARIANT
QDBusObjectPath OBJECT_PATH
QDBusSignature SIGNATURE

同时支持

QStringList, QByteArray

自定义类型

方式一:

struct MyStructure {
int count;
QString name;
};
Q_DECLARE_METATYPE(MyStructure)

// 服务端和客户端都要重载
QDBusArgument &operator<<(QDBusArgument &argument,const MyStructure &mystruct)
{
argument.beginStructure();
argument << mystruct.count << mystruct.name;
argument.endStructure();
return argument;
}

// Retrieve the MyStructure data from the D-Bus argument
const QDBusArgument &operator>>(const QDBusArgument &argument, MyStructure &mystruct)
{
argument.beginStructure();
argument >> mystruct.count >> mystruct.name;
argument.endStructure();
return argument;
}
qRegisterMetaType<MyStructure>("MyStructure");
qDBusRegisterMetaType<MyStructure>();

方式二:

利用QByteArray实现自定义类型

class MyClass {
private:
int count;
QString name;
}
QDataStream & operator<< (QDataStream& stream,const MyClass& myclass)
{
stream<<myclass.count;
stream<<myclass.name;
}
QDataStream & operator>> (QDataStream& stream,const MyClass& myclass)
{
stream>>myclass.count;
stream>>myclass.name;
}
服务端先把数据写入QByteArray。 客户端通过QDataStream把数据从QByteArray读出

编写Adaptor

如果注册对象时,使用QDBusConnection::ExportAllContents, 会导致很多的接口都暴露给用户。 为此, 可以编写Adaptor来控制接口

// adaptor.h
#include <QDBusAbstractAdaptor>
#include <QDebug>

class Adaptor :public QDBusAbstractAdaptor
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface","com.brion.interface")

Q_CLASSINFO("D-Bus Introspection",""
"<interface name=\"com.brion.interface\">\n"
" <method name=\"setAge\">\n"
" <arg type=\"i\" direction=\"in\"/>"
" </method>\n"
" <signal name=\"ageChanged\">\n"
" <arg type=\"i\" direction=\"out\"/>"
" </signal>"
"</interface>\n"
"")

public:
Adaptor::Adaptor(QObject* parent = 0) : QDBusAbstractAdaptor(parent) {
// 是否转发signals
setAutoRelaySignals(true);
}

signals:
void ageChanged(int age);

public slots:
void setAge(int age) {
QMetaObject::invokeMethod(parent(),"setAge", Q_ARG(int, age));
}
};

// person.h
#include <QObject>
class Person :public QObject
{
Q_OBJECT

public:
Person::Person(QObject *parent) : QObject(parent) {
m_age = 0;
new Adaptor(this);
}
signals:
void nameChanged(QString);
void ageChanged(int);

public slots:
QString name()const {return m_name; }
void setName(QString name) {
m_name = name;
}

int age()const {return m_age; }
void setAge(int age) {
m_age = age;
emit ageChanged(m_age);
}

private:
QString m_name;
int m_age;
};

XML数据类型定义

基本类型

type

code

type

code

BYTE y
BOOLEAN b
INT16 n
UINT16 q
INT32 i
UINT32 u
INT64 x
UINT64 t
DOUBLE d
STRING s
VARIANT v
OBJECT_PATH o

void setAge(int age)

<method name=\"setAge\">
<arg type=\"i\" direction=\"in\"/> // in 传参
</method>

int age() const

<method name=\"age\">
<arg type=\"i\" direction=\"out\"/> // out 返回值
</method>

void setName(QString name)

<method name=\"setName\">
<arg type=\"s\" direction=\"in\"/>
</method>

void setNames(QStringList names)

<method name=\"setNames\">
<arg type=\"as\" direction=\"in\"/>
</method>

void setNames(QByteArray ba)

<method name=\"setNames\">
<arg type=\"ay\" direction=\"in\"/>
</method>

自定义类型

类和结构体, 不允许空结构体

struct MyStruct
{
int key;
QString value;
}

void setCustom(MyStruct my)

<method name=\"setCustom\">
<arg type=\"(is)\" direction=\"in\"/>
</method> 
void setCustoms(QList<MyStruct> mystructs)
<method name=\"setCustoms\">
<arg type=\"a(is)\" direction=\"in\"/>
</method>
struct MyStruct
{
QMap<QString, QVariant> maps;
}
void setCustom(MyStruct my)
<method name=\"setCustom\">
<arg type=\"(a{sv})\" direction=\"in\"/>
</method>

键值队

void setDict(QMap<int, QString> dict);

<method name=\"setDict\">
<arg type=\"a{is}\" direction=\"in\"/>
</method>

权限控制

通过配置文件来实现权限控制。

<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<type>session/system</type>
<policy context="default">
<deny send_destination="com.brion.service"/>
</policy>
<policy user="service">
<!-- Only service user can own the "com.brion.service" service -->
<allow own="com.brion.service"/>
<allow send_destination="com.brion.service"/>
</policy>
<policy user="system">
<allow send_destination="com.brion.service"/>
</policy>
<policy group="systemapps">
<allow send_destination="com.brion.service" send_interface="com.brion.interface" send_member="setAge" send_path="/" send_type="method_call"/>
<allow send_destination="com.brion.service" send_interface="com.brion.interface" send_member="ageChanged" send_path="/" send_type="signal"/>
<deny send_destination="com.brion.service" send_interface="com.brion.service" send_member="age" send_path="/" send_type="method_call"/>
<deny send_destination="com.brion.service" send_interface="com.brion.interface" send_member="nameChanged" send_path="/" send_type="signal"/>
</policy>
<policy at_console="true">
<allow send_destination="xxx.xx.xx"/>
<allow send_interface="xxx.xx.xx"/>
</policy>
</busconfig>

所有 context=”default” 的策略被应用

所有 group=”connection’s user’s group” 的策略以不定的顺序被应用

所有 user=”connection’s auth user” 的策略以不定顺序被应用

所有 at_console=”true” 的策略被应用

所有 at_console=”false” 的策略被应用

所有 context=”mandatory” 的策略被应用

后应用的策略会覆盖前面的策略。

dbus 常用命令

dbus-send

调用函数

dbus-send --session --type=method_call  --dest=com.brion.service / com.brion.interface.setName "string:Brion"

发送信号

dbus-send --session --type=signal --dest=com.brion.service / com.brion.interface.ageChanged int32:10000

dbus-monitor

监听dbus-daemon的行为

手动启动dbus-daemon

DBUS_VERBOSE=1 dbus-daemon --session --print-address

启动后会得到一个地址

unix:abstract=/tmp/dbus-YcjSNNPJHg,guid=18b385acdbd58611ffd3196b4beb69f0

设置环境变量 DBUS_SESSION_BUS_ADDRESS

DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-YcjSNNPJHg,guid=18b385acdbd58611ffd3196b4beb69f0

再启动dbus server 和dbus client 都会用这个dbus-daemon 来通信。

附:示例代码:

dbus-demo


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

APP蓝图

APP蓝图

吕皓月 / 清华大学出版社 / 2015-1-1 / 69.00

移动互联网原型设计,简单来说,就是使用建模软件制作基于手机或者平板电脑的App,HTML 5网站的高保真原型。在7.0 之前的版本中,使用Axure RP进行移动互联网的建模也是可以的。比如,对于桌面的网站模型,制作一个1024像素宽度的页面就可以了;现在针对移动设备,制作320像素宽度的页面就好了。但是在新版本的Axure RP 7.0 中,加入了大量对于移动互联网的支持,如手指滑动,拖动,横屏......一起来看看 《APP蓝图》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码