DBus for IPC in Qt

Linux 系统IPC种类:

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

DBus 概念

总线

持久化的系统总线(system bus)
会话总线(session bus)

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仅仅是为了用来实现绑定。

优点

D-Bus是低延迟而且低开销的,设计得小而高效,以便最小化传送的往返时间。
另外,协议是二进制的,而不是文本的,这样就排除了费时的序列化过程。

D-Bus进程通信简单框架

DBus for IPC in Qt_第1张图片
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::ExportScriptableProperties
QDBusConnection::ExportNonScriptableInvokables // Q_INVOKABLE

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

例子:

Dbus Server

// person.h
#include 

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 
#include 
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_第2张图片
d-feet 查看dbus 服务

DBus Client

#include 
#include 
#include 
#include 
#include 

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

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

客户端调用服务端的函数

方式一

  // 传参数
  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不仅可以用来存储发送的参数,也用来存储返回值
      // 这里取得 checkIn 的返回值
      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 reply = interface.call("name");
    if(reply.isValid())
    {
        QString value = reply.value();
        qDebug()<<"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 reply = *call;
     if (!reply.isError()) {
         QString name= reply.argumentAt<0>();
         qDebug()<<"name = "<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
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");
 qDBusRegisterMetaType();

方式二:

利用QByteArray实现自定义类型

class MyClass
{
 private:
     int count;
     QString name;
}
QDataStream & operator<< (QDataStream& stream, const MyClass& myclass)
{
    stream<> (QDataStream& stream, const MyClass& myclass)
{
    stream>>myclass.count;
    stream>>myclass.name;
}

服务端先把数据写入QByteArray。 客户端通过QDataStream把数据从QByteArray读出

编写Adaptor

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

// adaptor.h
#include 
#include 

class Adaptor : public QDBusAbstractAdaptor
{
    Q_OBJECT
    Q_CLASSINFO("D-Bus Interface", "com.brion.interface")
    
    Q_CLASSINFO("D-Bus Introspection", ""
                "\n"
                "   \n"
                "       "
                "   \n"
                "   \n"
                "       "
                "   "
                "\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 
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
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)


   // in 传参

int age() const


   // out 返回值

void setName(QString name)


  

void setNames(QStringList names)


  

void setNames(QByteArray ba)


  

// 有待测试

自定义类型

类和结构体, 不允许空结构体
struct MyStruct
{
int key;
QString value;
}

void setCustom(MyStruct my)


  

void setCustoms(QList mystructs)


  

struct MyStruct
{
QMap maps;
}

void setCustom(MyStruct my)


  

键值队
void setDict(QMap dict);


  

权限控制

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



    session/system
    
        
    
    
        
        
        
    
    
        
    
    
          
          
          
          
    
    
        
        
    

所有 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 来通信。

参考:
http://blog.csdn.net/mznewfacer/article/details/7475265

你可能感兴趣的:(DBus for IPC in Qt)