1. 程式人生 > >如何高效率的使用DBUS作client/server架構

如何高效率的使用DBUS作client/server架構


在嵌入式系統中使用dbus主要有兩個方面的用途:
1:程序間通訊
2:實現client/server模式;

2也是1的具體表現形式;

包括dbus自帶的例子,都是採用dbus對資料的封裝,實現client/server模式的,
缺點有二:
1 一個API要定義一個xml介面描述
2 資料封裝非常複雜,非常不利於以後介面的擴充套件;

為了客服上面的缺點,提高可擴充套件性和效率,可以這樣做:
如果一個應用分為client,server兩端的話,要高效率的實現client/server之間
的通訊,可以採用如下方式:


第一步:定義一個通用的API xml 介面描述,暫命令為dbus_general.xml

<?xml version="1.0" encoding="UTF-8" ?>
<node name="/org/freedesktop/DBus/General_api">
  <interface name="org.freedesktop.DBus.general_api">
    <method name="client_request">
      <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="__client_request_cb"/>
      <arg type="i" name="action_id" direction="in" />   //這個地方就是不同API的ID
      <arg type="i" name="input_int" direction="in" />   //這個引數可以用,也可以不用
   <arg type="ay" name="input_garray" direction="in" />  //這個Garray用來從client傳遞資料,包括複雜的資料結構到server
   <arg type="i" name="outut_int" direction="out" />    //這個可以用,也可以不用
   <arg type="ay" name="output_garray" direction="out" /> //這個Garray用來從server側傳回資料到client側
      <arg type="i" name="result" direction="out" />
    </method>
  </interface>
</node>

大家知道:在dbus文件中有這麼的描述,

ay | Array of bytes | DBUS_TYPE_G_BYTE_ARRAY | GArray *  |g_array_free  

大家都不常用位元組陣列(GArray),大家常用的是integar,string等;
這個通用的模板關鍵之處就是這個Garray, Garray本身是個容器,這個
容器裡面可以裝任何東西。

我們就是利用這個GArray來實現client與server之間資料的傳遞,無論想傳遞
什麼要的資料;


第二步:用dbus的工具函式生成stub/proxy標頭檔案,這一步寫到Makefile指令碼中,以後不用修改了;

dbus-binding-tool --mode=glib-server --prefix=your_module_name dbus_general.xml > general_stub.h
dbus-binding-tool --mode=glib-client --prefix=your_module_name dbus_general.xml > general_proxy.h

生成的標頭檔案,大家一般不要動它們,直接使用就可以了;

general_proxy.h:

.....
client_request (DBusGProxy *proxy, const gint IN_action_id, const gint IN_input_int, const GArray* IN_input_garray, gint* OUT_output_int, GArray** OUT_output_garray, gint* OUT_result, GError **error)

{
  return dbus_g_proxy_call (proxy, "request", error, G_TYPE_INT, IN_action_id, G_TYPE_INT, IN_input_int, dbus_g_type_get_collection ("GArray", G_TYPE_UCHAR), IN_input_garray, G_TYPE_INVALID, G_TYPE_INT, OUT_output_int, dbus_g_type_get_collection ("GArray", G_TYPE_UCHAR), OUT_output_garray, G_TYPE_INT, OUT_result, G_TYPE_INVALID);
}
.....

general_stub.h:
.....

#include <dbus/dbus-glib.h>
static const DBusGMethodInfo dbus_glib_your_module_name_methods[] = {
  { (GCallback) __client_request_cb, dbus_glib_marshal_your_module_name_BOOLEAN__INT_INT_BOXED_POINTER_POINTER_POINTER_POINTER, 0 },
};

const DBusGObjectInfo dbus_glib_your_module_name_object_info = {
  0,
  dbus_glib_your_module_name_methods,
  1,
"org.freedesktop.DBus.general_api/0client_request/0S/0action_id/0I/0i/0input_int/0I/0i/0input_garray/0I/0ay/0output_int/0O/0F/0N/0i/0output_garray/0O/0F/0N/0ay/0result/0O/0F/0N/0i/0/0/0",
"/0",
"/0"
};
......

第三步:實現client側,主要是直接呼叫general_proxy.h的介面函式client_request(),用GArray傳入你的陣列(可以攜帶任何你自己定義的資料結構)

gboolean proxy_func1 (void)
{
 int api_id = 0;      //這個在不同的proxy_func裡面可以有不同的值,主要是區分函式作用
 GArray* in_array = NULL;
 GArray* out_array = NULL; //在這裡不用分配記憶體,放在server側做記憶體分配
 
 in_array = g_array_new(FALSE, FALSE, sizeof(guint8));
 if (!in_array)
  return FALSE;
  
 //把你自己的資料封裝到in_array中,假設你的資料結構是your_strcut_t
 
 your_struct_t my_own_data;
 
 //fill my_own_data
 ...
 
 //放到in_array中,這很關鍵
 g_array_append_vals(in_array, my_own_data, sizeof(your_strcut_t));  
 
 //呼叫general_proxy.h中的dbus介面
 client_request(dbus_proxy, api_id, in_array, &out_array, .....); //通過dbus把資料從到server側,server側如何處理,看第四步;
 
 //當sever返回資料後,從out_array中取出來就可以了
 your_strcut_t* g_array_data = (your_strcut_t*)out_array->data;
 .....
 
 //free
 if (in_array)
  g_free (in_array);
 
 if (out_array)
  g_free (out_array);
  
 ....


第四步:實現Server側,主要是實現general_stub.h中的函式__client_request_cb();

//這個函式的引數很長,除了第一個引數是server物件外,其餘的引數可以直接從
general_proxy.h對應的介面引數拷貝過來;應該這個函式和proxy的介面是一對!

gboolean
__client_request_cb (ServerObject *server_object, const gint IN_action_id, const gint IN_input_int, const GArray* IN_input_garray, gint* OUT_output_int, GArray** OUT_output_garray, gint* OUT_result, GError **error)
{
 *OUT_output_garray = g_array_new(FALSE, FALSE, sizeof(guint8));//在client側沒有分配記憶體,server這裡一定要分配
 
 //卸下client側傳遞過來的資料
 your_strcut_t ×p_data= (your_strcut_t *)&g_array_index(input_garray,your_strcut_t, 0);
 
 //對卸下的資料進行處理,看你的程式做什麼功能了:)
 .....
 .....
 
 //如果要傳回資料到client側,假設處理過的資料為:your_strcut_t dealed_with_data
 g_array_append_vals(*output_garray, &dealed_with_data, sizeof(your_strcut_t));
 
 
 return TRUE;//一定要返回TRUE,否則client側收不到資料的;
 
}


上述通用步驟中,1,2在今後的擴充套件中,是不要要改的,尤其是第一步,dbus的xml介面描述非常
麻煩;如果為每個API自己去定義xml介面描述,搞不好,client和server之間不通;而且,一段時間
後,不看dbus的文件,就會忘記如何寫其xml介面;所以做個通用的xml介面描述很省事;

3,4是client/server側的各自實現,結構是釘死的,不用改多少;一個函式如此,N個函式也是這樣;
如果你有30個函式,要分別實現它們嗎?不必要,只要給各自的函式定義其ID就行;
在client/server側的函式裡面搞個switch-case結構就分開了;

架構定好了,傳遞資料也非常方便,比dbus自己的dbus_g_type_struct_set效率高的多,目前開源軟體
多用dbus_g_type_struct_set,效率很低,對於傳遞批量資料,效率很低;

如果大家對於如何提高dbus傳遞訊息/資料的效率,有什麼更好的看法,歡迎交流。