DBus通讯

linux下进程之中通信的章程要有Pipe(管道),FIFO(命名管道),信号,共享内存,消息队列,信号灯等,这些艺术各发
各得特点,如管道是linux下命令行中常用的,用于父子进程的通信。但是这些通信方式还较老,要属功能最好有力的IPC应该是dbus,故查看了一晃
dbus的材料,但是材料相对比较少,特别是关于python的一些。 1.dbus概念

网上发出同首被“D-Bus
Tutorial”的章,流传于常见。

D-Bus是针对性桌面环境优化的IPC(interprocess communication
)机制,用于进程中的通信或进程同基本的通信。最核心的D-Bus协议是一对一底通信协议。但在众情下,通信的一致着是信息总线。消息总线是一个新鲜之
应用,它又和大多独下通信,并以运用内传递消息。下面我们见面于实例中观测消息总线的图。消息总线的角色有些类似与X系统中之窗口管理器,窗口管理器
既是X客户,又负责管理窗口。

支撑dbus的系统还出个别单专业的音总线:系统总线和对话总线。系统总线用于系和使用之通信。会话总线用于采取中的通信。网上发一个为d-feet的python程序,我们好据此其来观网面临的dbus世界。

图片 1

图1、由d-feet观察到的D-Bus世界

dbus还提供了零星个命令行工具用于dbus测试,dbus-send和dbus-monitor,前一个指令用于测试信号的发送,后一个令用于监控dbus的数据流。

2.dbus概念

至于dbus的基础知识不在本文的限制外,具体的参见dbus的文档。下面为出dbus常用的流水线。

2.1树服务之流程

 dbus_bus_get(),建立一个dbus连接;

 dbus_bus_request_name(),为之dbus连接(DbusConnection)起名,这个名字将见面成我们在继续开展长距离调用的早晚的劳动名;

下一场我们进入监听循环 — dbus_connection_read_write();

      从总线上取出消息 — dbus_connection_pop_message();

      并透过比对信息备受之方式接口名以及章程名 —
dbus_message_is_method_call();

      如果同,那么我们越反到相应的拍卖面临错过;

    
 在相应的处理面临,我们会从消息受到取出远程调用的参数。并且成立于回传结果的通路
—     
reply_to_method_call()。回传动作本身一样于一致不好无待等待结果的长距离调用。

2.2建立劳动的流水线

成立好dbus连接之后,为当时dbus连接命名,申请一个远程调用通道 —
dbus_message_new_method_call(),注意,在申请远程调用通道的下,需要填写服务器名,本次调用的接口名,和本次调用名
(方法名)。压入本次调用的参数 — dbus_message_iter_init_append();
dbus_message_iter_append_basic(),实际上是申请了一个首地点,我们就是将咱确实要传的参数,往这个首地方间送(送

完之后一般都见面咬定是否内存越界了)。然后就是是启动发送调用并释放发送有关的消息结构

dbus_connection_send_with_reply()。这个启动函数中蕴含一个句柄。我们马上会阻塞等待这词柄给咱带来回总线上回传的
消息。当以此词柄回传消息之后,我们从信息结构中分别有参数。用dbus提供的函数提取参数的色和参数
— dbus_message_iter_init(); dbus_message_iter_next();
dbus_message_iter_get_arg_type();
dbus_message_iter_get_basic()。也不怕达了咱开展此次远程调用的目的了。
 

2.3出殡信号的流程

 建立一个dbus连接之后,为这dbus连接起名,建立一个殡葬信号的大路,注意,在成立通道的函数中,需要我们填写该信号的接口名与信号名
— dbus_message_new_signal()。然后我们拿信号对应之有关参数压进 —
dbus_message_iter_init_append();
dbus_message_iter_append_basic()。然后就是得启动发送了 —
dbus_connection_send(); dbus_connection_flush。

2.4信号接收流程

建立一个dbus连接之后,为这dbus连接起名,为我们将进行的消息循环添加匹配原则(就是通过信号名和信号接口名来拓展匹配控制的)

dbus_bus_add_match()。我们进来等循环后,只待对信号名,信号接口名展开判断即便可以独家处理各种信号了。在逐一处理分支上。我们
可以分别有消息遭之参数。对参数类型进行判断及另的拍卖。
 

  1. 一个C语言的演示代码

网上大部分代码都是冲dbus的一个封装库libdbus做的,以及以glib,gtk的风波循环;为了减少库的指,直接行使C语言调用dbus的平底函数编写一个远距离调用的示范代码,代码很简短,没动用GObject等有繁杂的库房。

远程调用的服务器代码,用于监控,代码如下:

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#include
#include
#include
#include
#include
 
void reply_to_method_call(DBusMessage* msg, DBusConnection* conn)
{
   DBusMessage* reply;
   DBusMessageIter args;
   bool stat = true;
   dbus_uint32_t level = 21614;
   dbus_uint32_t serial = 0;
   char* param = "";
 
   // read the arguments
   if (!dbus_message_iter_init(msg, &args))
      fprintf(stderr, "Message has no arguments!\n");
   else if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&args))
      fprintf(stderr, "Argument is not string!\n");
   else
      dbus_message_iter_get_basic(&args, ¶m);
 
   printf("Method called with %s\n", param);
 
   // create a reply from the message
   reply = dbus_message_new_method_return(msg);
 
   // add the arguments to the reply
   dbus_message_iter_init_append(reply, &args);
   if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_BOOLEAN, &stat)) {
      fprintf(stderr, "Out Of Memory!\n");
      exit(1);
   }
   if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &level)) {
      fprintf(stderr, "Out Of Memory!\n");
      exit(1);
   }
 
   // send the reply && flush the connection
   if (!dbus_connection_send(conn, reply, &serial)) {
      fprintf(stderr, "Out Of Memory!\n");
      exit(1);
   }
   dbus_connection_flush(conn);
 
   // free the reply
   dbus_message_unref(reply);
}
 
 
static void
reply_to_Introspect(DBusMessage* msg, DBusConnection* conn)
{
    /*反馈的消息*/
    char *xml = "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
    "\n"
    "  \n"
    "    \n"
    "      \n"
    "    \n  \n"
    "  \n"
    "    \n"
    "      \n"
    "      \n"
    "    \n"
    "  \n"
    "\n";
    DBusMessage* reply;
   DBusMessageIter args;
   bool stat = true;
    
   // create a reply from the message
   reply = dbus_message_new_method_return(msg);
   // add the arguments to the reply
   dbus_message_iter_init_append(reply, &args);
    
   if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &xml)) {
      printf ("Dbus Error: append args error\n");
      dbus_message_unref(reply);
      return;
   }
   // send the reply && flush the connection
   if (!dbus_connection_send(conn, reply, NULL)) {
      printf ("Dbus Error: send error\n");
      dbus_message_unref(reply);
      return;
   }
   dbus_connection_flush(conn);
   // free the reply
   dbus_message_unref(reply);
}
 
/**
* Server that exposes a method call and waits for it to be called
*/
void listen()
{
   DBusMessage* msg;
   DBusMessage* reply;
   DBusMessageIter args;
   DBusConnection* conn;
   DBusError err;
   int ret;
   char* param;
 
   printf("Listening for method calls\n");
 
   // initialise the error
   dbus_error_init(&err);
    
   // connect to the bus and check for errors
   conn = 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 (NULL == conn) {
      fprintf(stderr, "Connection Null\n");
      exit(1);
   }
    
   // request our name on the bus and check for errors
   ret = dbus_bus_request_name(conn, "test.method.server",
                               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 (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret) {
      fprintf(stderr, "Not Primary Owner (%d)\n", ret);
      exit(1);
   }
 
   // loop, testing for new messages
   while (true) {
      // non blocking read of the next available message
      dbus_connection_read_write(conn, 0);
      msg = dbus_connection_pop_message(conn);
 
      // loop again if we haven’t got a message
      if (NULL == msg) {
         sleep(1);
         continue;
      }
       
      // check this is a method call for the right interface & method
      if (dbus_message_is_method_call(msg, "test.method.Type", "Method"))
         reply_to_method_call(msg, conn);
      /*实现反射接口*/
      if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Introspectable", "Introspect"))
     reply_to_Introspect(msg, conn);
      // free the message
      dbus_message_unref(msg);
   }
}
 
 
 
 
int main(int argc, char** argv)
{
 
   listen();
   return 0;
}

代码中杀要紧之一个地方是一个标准接口的实现,该接口虽说无实际意义,仅仅是反射出该session的接口信息,包含各个接口信息以及信号信息,但是该消息在python版的dbus中调用很重大,否则python的调用会失败。

编译命令如下

?

1
gcc -o main main.c `pkg-config –cflags –libs dbus-1`

足就此d-feet测试一下:

图片 2

因此dbus-send测试命令如下:

?

1
dbus-send –session –type=method_call –print-reply –dest=test.method.server / test.method.Type.Method

 

客户端代码(及远程调用的代码):

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include
#include
#include
#include
#include
/** 
 * Call a method on a remote object 
 */ 
void query(char* param)   
{  
   DBusMessage* msg;  
   DBusMessageIter args;  
   DBusConnection* conn;  
   DBusError err;  
   DBusPendingCall* pending;  
   int ret;  
   bool stat;  
   dbus_uint32_t level;  
   
   printf("Calling remote method with %s\n", param);  
   
   // initialiset the errors  
   dbus_error_init(&err);  
   
   // connect to the system bus and check for errors  
   conn = 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 (NULL == conn) {   
      exit(1);   
   }  
   
   // request our name on the bus  
   ret = dbus_bus_request_name(conn, "test.method.caller", 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 (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret) {   
      exit(1);  
   }  
   
   // create a new method call and check for errors  
   msg = dbus_message_new_method_call("test.method.server", // target for the method call  
                                      "/test/method/Object", // object to call on  
                                      "test.method.Type", // interface to call on  
                                      "Method"); // method name  
   if (NULL == msg) {   
      fprintf(stderr, "Message Null\n");  
      exit(1);  
   }  
   
   // append arguments  
   dbus_message_iter_init_append(msg, &args);  
   if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, ¶m)) {  
      fprintf(stderr, "Out Of Memory!\n");   
      exit(1);  
   }  
       
   // send message and get a handle for a reply  
   if (!dbus_connection_send_with_reply (conn, msg, &pending, -1)) { // -1 is default timeout  
      fprintf(stderr, "Out Of Memory!\n");   
      exit(1);  
   }  
   if (NULL == pending) {   
      fprintf(stderr, "Pending Call Null\n");   
      exit(1);   
   }  
   dbus_connection_flush(conn);  
       
   printf("Request Sent\n");  
       
   // free message  
   dbus_message_unref(msg);  
       
   // block until we recieve a reply  
   dbus_pending_call_block(pending);  
   
   // get the reply message  
   msg = dbus_pending_call_steal_reply(pending);  
   if (NULL == msg) {  
      fprintf(stderr, "Reply Null\n");   
      exit(1);   
   }  
   // free the pending message handle  
   dbus_pending_call_unref(pending);  
   
   // read the parameters  
   if (!dbus_message_iter_init(msg, &args))  
      fprintf(stderr, "Message has no arguments!\n");   
   else if (DBUS_TYPE_BOOLEAN != dbus_message_iter_get_arg_type(&args))   
      fprintf(stderr, "Argument is not boolean!\n");   
   else 
      dbus_message_iter_get_basic(&args, &stat);  
   
   if (!dbus_message_iter_next(&args))  
      fprintf(stderr, "Message has too few arguments!\n");   
   else if (DBUS_TYPE_UINT32 != dbus_message_iter_get_arg_type(&args))   
      fprintf(stderr, "Argument is not int!\n");   
   else 
      dbus_message_iter_get_basic(&args, &level);  
   
   printf("Got Reply: %d, %d\n", stat, level);  
       
   // free reply   
   dbus_message_unref(msg);     
}  
   
 
 
 
int main(int argc, char** argv)
{
    char* param = "no param"; 
    query(param);
    return 0;
}

 执行结果:

Calling remote method with no param
Request Sent
Got Reply: 1, 21614
 

4.Pthon调用dbus

?

1
2
3
4
5
6
7
8
9
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import dbus
bus = dbus.SessionBus()
bus_obj = bus.get_object(‘test.method.server’, ‘/’)
 
interface = dbus.Interface(bus_obj, ‘test.method.Type’)
info = interface.Method()
print info