libdbus 实例以及使用d-feet查看接口方法

libdbus 实例以及使用d-feet查看接口方法

libdbus介绍

总线

​ linux系统进程间通过dbus通信,D-BUS由总线构成,总线分为两种,系统总线(system-bus)和会话总线(session-bus)。系统总线在引导时就会启动,这个总线由操作系统和后台进程使用,安全性非常好,以使得任意的应用程序不能欺骗系统事件.会话总线在用户登陆后启动,属于用户私有,是用户进程用来通信的一个会话总线。如果一个应用程序需要接收来自系统总线的消息,可以直接连接到系统总线 —— 不过,它可以发送的消息将是受限的。只有Linux内核、Linux桌面环境和权限较高的程序才能向系统总线写入消息,以此保障系统安全性,防止有恶意进程假冒Linux发送消息。

服务名

​ 每个应用程序连接到总线后,指定一个服务名,bus name;

对象名

​ 消息发送到指定的对象,对象格式类似路径名称:/com/jimi/dbus

接口名

​ 一组功能的集合名字,com.jimi.test,接口下实现很多方法或者信号

消息类型

​ 在 D-BUS 中有四种类型的消息:方法调用(method calls)、方法返回(method returns)、信号(signals)和错误(errors)。要执行 D-BUS 对象的方法,您需要向对象发送一个方法调用消息。它将完成一些处理(就是执行了对象中的Method,Method是可以带有输入参数的。)并返回,返回消息或者错误消息。信号的不同之处在于它们不返回任何内容:既没有“信号返回”消息,也没有任何类型的错误消息

signal

​ 发送信号,接收者获取信号,并响应信号的事件,信号不返回数据。

method

​ 客户端调用服务端的方法,有输入,有输出。

当一个应用连接到bus daemon,daemon立即会分配一个名字给这个连接,称为unique connection name ,这个唯一标识的名字以冒号:开头,例如:34-907,这个名字在daemon的整个生命周期是唯一的。但是这种名字总是临时分配,无法确定的,也难以记忆,因此应用可以要求有另外一个名字well-known name 来对应这个唯一标识,就像我们使用域名来对应IP地址一样。例如可以使用com.mycompany来映射:34-907。

Address –> [BusName] –> Path –> Interface –> Method

bus name不是必要的,它只在daemon的情况下用于路由,点对点的直接连接是不需要的。

​ 简单地说:Address是D-Bus中server用来监听client的地址,当一个client连接上D-Bus,通常是Daemo的方式,这个client就有了一个Bus Name。其他应用可以根据消息中所带的Bus Name,来判断和哪个应用相关。消息在总线中传递的时候,传递到应用中,再根据objectpath,送至应用中具体的对象实例中,也就是是应用中根据Interface创建的对象。这些Interface有method和singal两种,用来发送、接收、响应消息。

消息通过D-Bus在进程间传递。有四类消息:

一、Method call消息:将触发对象的一个method

二、Method return消息:触发的方法返回的结果

三、Error消息:触发的方法返回一个异常

四、Signal消息:通知,可以看作为事件消息。

libdbus 服务端demo



#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace  std;
using json = nlohmann::json;
static DBusConnection *connect;

const char * object_path = "/com/jimi/dbus";
const char * signal_object_path = "/signal/jimi/dbus";
const char * method_interface = "ipc.method";
const char * signal_interface = "ipc.signal";
void handle_unregister(DBusConnection * connection, void * user_data);
DBusHandlerResult message_handler(DBusConnection * connection,DBusMessage * msg,void * user_data);
int dbus_send_signal(DBusConnection * connection,const char *signalName, char *value);

static DBusObjectPathVTable server_path_vt = {
    .unregister_function = handle_unregister,
    .message_function = message_handler
};

typedef struct {
    dbus_uint32_t id;
}ConnectData;

ConnectData server_data = {0};

void reply_to_method_call(DBusMessage * msg, DBusConnection * conn){
    DBusMessage * reply;
    DBusMessageIter arg;
    char * param = NULL;
    dbus_bool_t stat = TRUE;
    dbus_uint32_t level = 2010;
    dbus_uint32_t serial = 0;

    //从msg中读取参数,这个在上一次学习中学过
    if(!dbus_message_iter_init(msg,&arg))
        printf("Message has no args/n");
    else if(dbus_message_iter_get_arg_type(&arg) != DBUS_TYPE_STRING)
        printf("Arg is not string!/n");
    else
        dbus_message_iter_get_basic(&arg,&param);
        printf("%s",param);
    if(param == NULL) return;

    string str_reply = param;
    //创建返回消息reply
    reply = dbus_message_new_method_return(msg);
    //在返回消息中填入两个参数,和信号加入参数的方式是一样的。这次我们将加入两个参数。
    dbus_message_iter_init_append(reply,&arg);
    if(!dbus_message_iter_append_basic (&arg,DBUS_TYPE_BOOLEAN,&stat)){
        printf("Out of Memory!/n");
        exit(1);
    }
    if(!dbus_message_iter_append_basic (&arg,DBUS_TYPE_UINT32,&level)){
        printf("Out of Memory!/n");
        exit(1);
    }
    if(!dbus_message_iter_append_basic (&arg,DBUS_TYPE_STRING,&str_reply)){
        printf("Out of Memory!/n");
        exit(1);
    }
  //发送返回消息
      if( !dbus_connection_send (conn, reply, &serial)){
        printf("Out of Memory/n");
        exit(1);
    }
    dbus_connection_flush (conn);
    dbus_message_unref (reply);
}

void reply_to_introspec(DBusMessage * msg, DBusConnection * conn){
    DBusMessage * reply;
    DBusMessageIter arg;

    dbus_uint32_t serial = 0;

    string str_reply;

    //读取xml,xml写入了path下的所有接口及方法,d-feet可以查询
    ifstream in("/home/uos/dbus_test.xml");
    stringstream buffer;
    buffer<<in.rdbuf();
    str_reply = buffer.str();
    printf("%s",str_reply.c_str());
    //创建返回消息reply
    reply = dbus_message_new_method_return(msg);

    dbus_message_iter_init_append(reply,&arg);

    if(!dbus_message_iter_append_basic (&arg,DBUS_TYPE_STRING,&str_reply)){
        printf("Out of Memory!/n");
        exit(1);
    }
  //发送返回消息
      if( !dbus_connection_send (conn, reply, &serial)){
        printf("Out of Memory/n");
        exit(1);
    }
    dbus_connection_flush (conn);
    dbus_message_unref (reply);
}

DBusHandlerResult message_handler(DBusConnection * connection,DBusMessage * msg,void * user_data)
{
    DBusMessageIter arg;
    char * sigvalue;
    if(!msg){
        return DBUS_HANDLER_RESULT_HANDLED;
    }

    switch(dbus_message_get_type(msg)){
    case DBUS_MESSAGE_TYPE_METHOD_CALL:
    {
        if(dbus_message_is_method_call(msg,method_interface,"test_method")){
            auto path = dbus_message_get_path (msg);
            if(strcmp(path,object_path) == 0)
            {
                reply_to_method_call(msg, connection);
            }
         }
        else if(dbus_message_is_method_call(msg, DBUS_INTERFACE_INTROSPECTABLE,"Introspect")){ //"org.freedesktop.DBus.Introspectable"
            auto path = dbus_message_get_path (msg);
            if(strcmp(path,object_path) == 0)
            {
                reply_to_introspec(msg, connection);
            }
         }
        break;
    }
    case DBUS_MESSAGE_TYPE_SIGNAL:
    {
        if(dbus_message_is_signal(msg, signal_interface, "test_signal")){
            if(!dbus_message_iter_init(msg,&arg))
                fprintf(stderr,"Message Has no Param");
            else if(dbus_message_iter_get_arg_type(&arg) != DBUS_TYPE_STRING)
                fprintf(stderr,"Param is not string");
            else
            {
                dbus_message_iter_get_basic(&arg,&sigvalue);
                //信号内容
                printf("get signal content is : %s",sigvalue);
                fflush(stdout);
                //dbus_send_signal(connection, "test_signal", sigvalue);

                reply_to_method_call(msg, connection);
            }
        }
        break;
    }
    case DBUS_MESSAGE_TYPE_METHOD_RETURN:
    {
        return DBUS_HANDLER_RESULT_HANDLED;
    }
    case DBUS_MESSAGE_TYPE_INVALID:
    {
        return DBUS_HANDLER_RESULT_HANDLED;
    }
    default:
        return DBUS_HANDLER_RESULT_HANDLED;
    }

}

void handle_unregister(DBusConnection * connection, void * user_data){
    dbus_free(user_data);
}

void dbus_server()
{
    DBusMessage * msg;
    DBusMessageIter arg;
    DBusConnection * connection;
    DBusError err;
    int ret;
    char * sigvalue;

    dbus_error_init(&err);
    //创建于session D-Bus的连接
    connection = dbus_bus_get(DBUS_BUS_SESSION, &err);
    if(dbus_error_is_set(&err)){
        fprintf(stderr,"Connection Error %s/n",err.message);
        dbus_error_free(&err);
    }
    if(connection == NULL)
        return;
    //设置一个BUS name:test.wei.dest
    ret = dbus_bus_request_name(connection,"com.jimi.dbus",DBUS_NAME_FLAG_REPLACE_EXISTING,&err);
    if(dbus_error_is_set(&err)){
        fprintf(stderr,"Name Error %s/n",err.message);
        dbus_error_free(&err);
    }
    if(ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
        return;
    ret = dbus_connection_register_object_path(connection,object_path,&server_path_vt,&server_data);
    if(dbus_error_is_set(&err)){
        fprintf(stderr,"Name Error %s/n",err.message);
        dbus_error_free(&err);
    }

    //消息循环
    while(dbus_connection_read_write_dispatch(connection,-1)){

    }

}
int main(){
    dbus_server();
    return 0;
}

以上是一个完整的dbus服务器示例,可以通过d-feet查看接口方法,如果你写的dbus程序在d-feet下看不到对象路径,功能方法调用正常,则是因为没有实现总线的标准接口 org.freedesktop.DBus.Introspectable 的方法Introspect,具体实现如上述代码,introspect方法返回的是xml数据,xml数据定义了接口-方法-参数-类型-入\出方向,d-feet能够显示dbus-server的接口方法,是因为调用了标准接口的Instrospect回显了所有的方法和信号。
libdbus 实例以及使用d-feet查看接口方法_第1张图片
dbus_test.xml内容定义如下:

DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
 "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
 <node>
   <interface name="ipc.signal">
     <signal name="test_signal">
       <arg name="info" type="s" direction="out"/>
     signal>
	interface>
   <interface name="ipc.method">
     <method name="test_method">
	 <arg name="input" type="s" direction="in"/>
     method>
	interface>
	<interface name="org.freedesktop.DBus.Introspectable">
	<method name="Introspect">
		<arg name="xml_data" type="s" direction="out"/>
	method>
	interface>
	<interface name="org.freedesktop.DBus.Properties">
	<method name="Get">
		<arg name="interface" type="s" direction="in"/>
		<arg name="propname" type="s" direction="in"/>
		<arg name="value" type="s" direction="out"/>
	method>
	<method name="Set">
		<arg name="interface" type="s" direction="in"/>
		<arg name="propname" type="s" direction="in"/>
		<arg name="value" type="s" direction="in"/>
	method>
	<method name="GetAll">
		<arg name="interface" type="s" direction="in"/>
		<arg name="props" type="a{sv}" direction="out"/>
	method>
	interface>
 node>

dbus-send 用法

Usage: dbus-send [--help] [--system | --session | --bus=ADDRESS | --peer=ADDRESS] [--dest=NAME] [--type=TYPE] [--print-reply[=literal]] [--reply-timeout=MSEC] <destination object path> <message name> [contents ...]

–system root用户进程dbus

–session 用户进程dbus
–print-reply 打印输出

–type=method_call / signal

–dest 目的服务名 方法路径 接口+method 参数类型:参数值

方法测试

在这里插入图片描述

信号测试

在这里插入图片描述

你可能感兴趣的:(C++,linux,服务器,网络)