DBus研究笔记(二)
一. 关于DBus Binding
DBus binding是对DBus lower-api 的一次封装操作, 通过这一机制, 我们能够获得更便捷的接口, 其中某些特定的binding还集成了一个事件循环, 使得使用更为简便。本节重点介绍Glib绑定的相关实现细节。
二. 一些基本概念
- 代理(proxy)
代理则将远程进程中某个具体对象的行为以本地对象的形式表现出来, 在之前的底层API消息传递过程中, 我们是手动创建消息后发送并等待响应, 而通过代理机制, 实现了一个本地对象式的操作方式: 发起一个方法调用, 系统会自动转化为消息发送给远程进程并获取返回值。 - GObject
GObject 是一种基于C语言开发的面向对象函数库, 具备跨语言兼容性功能。它是GNOME软件包的核心组件之一, 在GTK+等图形界面库中有广泛应用。值得注意的是,GTK+2.0之前的版本中包含这部分内容, 后来开发者将其大部分与图形界面无关的功能移至GObject模块, 更详细的资料可参考http://zh.wikipedia.org/wiki/GObject。
三.利用XML描述DBus接口
为了实现GObject对象之间的DBus通信, 我们介绍一个基本方法.dbus-bindings-tool基于XML文件来构建服务器与客户端代码.
XML接口描述文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE node PUBLIC
"-//freedesktop//DTD D-Bus Object Introspection 1.0//EN"
"http://standards.freedesktop.org/dbus/1.0/introspect.dtd">
<node>
<interface name="org.maemo.Value">
<method name="getvalue1">
<arg type="i" name="cur_value" direction="out"/>
</method>
<method name="getvalue2">
<arg type="d" name="cur_value" direction="out"/>
</method>
<method name="setvalue1">
<arg type="i" name="new_value" direction="in"/>
</method>
<method name="setvalue2">
<arg type="d" name="new_value" direction="in"/>
</method>
</interface>
</node>
代码解释
在 org.maemo.Value 接口中实现了一系列功能模块,并提供了用于获取和设置变量值的方法。每个 Method 都以 method 作为关键字并以一/method 的形式结束;其中 interface 与 node 类似,在这种情况下 node 可被视为一个对象;arg标识所使用的参数;type 表示参数的类型;name 表示名称;direction 表示方向;out标识返回值;你可以自定义地添加任意数量的 interface 和 method 等等;需要注意的是, dbus-binding-tool 工具会自动为我们添加 introspection 接口(默认情况下);另我们需要定义文件类型的 DTD 以便于校验工具能够方便地识别文件。
Makefile文件:
interface_xml := value-dbus-interface.xml
checkxml: $(interface_xml)
@(xmllint) --valid --noout $(interface_xml)
@(echo) $< check out ok
代码解释
说明:可用于一个名为xmllint的XML文件校验工具,在编辑XML时能够便捷地检测出编写过程中存在的语法问题。
lc@lc-Lenovo:~/DBus研究笔记$ make checkxml
value-dbus-interface.xml check out ok
代码解释
四.生成中间胶和代码(glub code)
改写说明
files := client-glub.h\
server-glub.h
client-glub.h:$(interface_xml)
dbus-binding-tool --prefix=value_object --mode=glib-client $< > $@
server-glub.h:$(interface_xml)
dbus-binding-tool --prefix=value_object --mode=glib-server $< > $@
代码解释
说明:--prefix指定生成的glub code的前缀,--mode指定是客户端还是服务器。
lc@lc-Lenovo:~/DBus研究笔记$ make value-server-stub.h value-client-stub.h
dbus-binding-tool --prefix=value_object --mode=glib-server \
value-dbus-interface.xml > value-server-stub.h
dbus-binding-tool --prefix=value_object --mode=glib-client \
value-dbus-interface.xml > value-client-stub.h
lc@lc-Lenovo:~/DBus研究笔记$ ls
DBus研究笔记(二) Makefile value-dbus-interface.xml
DBus研究笔记(一) value-client-stub.h value-server-stub.h
代码解释
在生成胶及后续代码的过程中,在首发信号触发时能够较为便捷地调用。下面将重点阐述几个关键问题:首先介绍的是marshal(列集)这一功能是如何将数据转换为流式格式进行处理;而unmarshaling(散集)则是其逆过程,在编译过程中,默认情况下推荐使用DBus绑定工具来自动生成相应的解密功能——从而实现了对散列集合(散集)的支持。此外,GTK库内置了大量标准实现,这些功能的具体定义均包含于头文件gmarshal.h中。对于那些未能满足需求的情况,我们还可以借助一个名为glib-genmarshal的工具,动态生成所需的特定实现模块
五.创建GObject对象
每一个GObject对象均有一个实例类别以及一个对象类别,在该对齐过程中涉及的所有实例都与所属的对象类别共享相同的属性;而属于该对齐过程的各个独立个体则分别拥有独特的属性。
typedef struct {
/* The parent class object state. */
GObject parent;
/* Our first per-object state variable. */
gint value1;
/* Our second per-object state variable. */
gdouble value2;
} ValueObject;
/* Per class state.
typedef struct {
/* The parent class state. */
GObjectClass parent;
} ValueObjectClass;
代码解释
注释:这边我们在实例类中声明了两个数据成员,并计划实现method来操作这两个变量。
GType value_object_get_type(void);
/* 定义一些GOBJECT 对象常用的宏 */
#define VALUE_TYPE_OBJECT (value_object_get_type())
#define VALUE_OBJECT(object) \
(G_TYPE_CHECK_INSTANCE_CAST((object), \
VALUE_TYPE_OBJECT, ValueObject))
#define VALUE_OBJECT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), \
VALUE_TYPE_OBJECT, ValueObjectClass))
#define VALUE_IS_OBJECT(object) \
(G_TYPE_CHECK_INSTANCE_TYPE((object), \
VALUE_TYPE_OBJECT))
#define VALUE_IS_OBJECT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass), \
VALUE_TYPE_OBJECT))
#define VALUE_OBJECT_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS((obj), \
VALUE_TYPE_OBJECT, ValueObjectClass))
/* Utility macro to define the value_object GType structure. */
G_DEFINE_TYPE(ValueObject, value_object, G_TYPE_OBJECT)
代码解释
method声明:
gboolean value_object_getvalue1(ValueObject* obj, gint* value_out,
GError** error);
gboolean value_object_getvalue2(ValueObject* obj, gdouble* value_out,
GError** error);
gboolean value_object_setvalue1(ValueObject* obj, gint value_in,
GError** error);
gboolean value_object_setvalue2(ValueObject* obj, gdouble value_in,
GError** error);
代码解释
实例类初始化:
static void value_object_init(ValueObject* obj) {
dbg("Called");
g_assert(obj != NULL);
obj->value1 = 0;
obj->value2 = 0.0;
}
代码解释
对象类初始化:
static void value_object_class_init(ValueObjectClass* klass) {
dbg("Called");
g_assert(klass != NULL);
dbg("Binding to GLib/D-Bus");
/* 安装自定义对象. */
dbus_g_object_type_install_info(VALUE_TYPE_OBJECT,
&dbus_glib_value_object_object_info);
dbg("Done");
}
代码解释
DBUS路径接口定义:
/* Well-known name for this service. */
#define VALUE_SERVICE_NAME "org.maemo.Platdev_ex"
/* Object path to the provided object. */
#define VALUE_SERVICE_OBJECT_PATH "/GlobalValue"
/* And we're interested in using it through this interface.
This must match the entry in the interface definition XML. */
#define VALUE_SERVICE_INTERFACE "org.maemo.Value"
代码解释
主函数:
#include "common-defs.h"
int main(int argc, char** argv) {
/* 初始化对象系统. */
g_type_init();
g_print(PROGNAME ":main Connecting to the Session D-Bus.\n");
bus = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
if (error != NULL) {
handleError("Couldn't connect to session bus", error->message, TRUE);
}
代码解释
g_print(PROGNAME ":main Registering the well-known name (%s)\n",
VALUE_SERVICE_NAME);
4. /* 注册代理
DBUS_SERVICE_DBUS = "org.freedesktop.DBus"
DBUS_PATH_DBUS = "/org/freedesktop/DBus"
DBUS_INTERFACE_DBUS = "org.freedesktop.DBus" */
busProxy = dbus_g_proxy_new_for_name(bus,
DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS);
if (busProxy == NULL) {
handleError("Failed to get a proxy for D-Bus",
"Unknown(dbus_g_proxy_new_for_name)", TRUE);
}
17. /* 申请名称. */
if (!dbus_g_proxy_call(busProxy,
"RequestName",
&error,
G_TYPE_STRING,
VALUE_SERVICE_NAME,
0,
G_TYPE_INVALID,
G_TYPE_UINT,
&result,
G_TYPE_INVALID)) {
handleError("D-Bus.RequestName RPC failed", error->message,
TRUE);
}
33. g_print(PROGNAME ":main RequestName returned %d.\n", result);
if (result != 1) {
handleError("Failed to get the primary well-known name.",
"RequestName result != 1", TRUE);}
代码解释
valueObj = g_object_new(VALUE_TYPE_OBJECT, NULL);
if (valueObj == NULL) {
handleError("Failed to create one Value instance.",
"Unknown(OOM?)", TRUE);
}
g_print(PROGNAME ":main Registering it on the D-Bus.\n");
dbus_g_connection_register_g_object(bus,
VALUE_SERVICE_OBJECT_PATH,
G_OBJECT(valueObj));
13. g_print(PROGNAME ":main Ready to serve requests (daemonizing).\n");
15. }
代码解释
最后编译利用dbus-send发送消息调用Method进行测试。
