1.写出.xml文件,描述自己的接口或信号
<?xml version="1.0" encoding="UTF-8"?>
<node name="/com/zhang/MyObject"> /*对象路径名*/
<interface name="org.zhang.Test.Basic"> /*接口名*/
<method name="Add"> /*方法Add*/
<arg name="input0" type="i" direction="in"/>
<arg name="input1" type="i" direction="in"/>
<arg name="output" type="i" direction="out"/>
</method>
<method name="Sub"> /*方法Sub*/
<arg name="arg0" type="u" direction="in"/>
<arg name="arg1" type="u" direction="in"/>
<arg name="ret" type="u" direction="out"/>
</method>
<signal name="Hello"> /*信号,用于发送一个字符串*/
<arg name="signal_hello" type="s"/>
</signal>
<signal name="Coordinate"> /*信号,用于发送两个G_TYPE_INT的整数*/
<arg name="coordinate0" type="i"/>
<arg name="coordinate1" type="i"/>
</signal>
</interface>
</node>
我们要在连接"com.zhang.test"中实现对象"/com/zhang/MyObject".这个对象有一个接口"org.zhang.Test.Basic"。这个接口有两个方法(Add和Sub),和两个信号(Hello和Coordinate).
2、由接口描述文件生成绑定文件
使用dbus-binding-tool的工具,它读入接口描述文件,产生一个绑定文件。这个文件包含了dbus对象的接口信息。在主程序中我们通过 dbus_g_object_type_install_info函数向dbus-glib登记对象信息(DBusGObjectInfo结构)。
使用dbus-bingding-tool命令
dbus-binding-tool --mode=glib-server --prefix=com_zhang server.xml >server_glue.h
--mode为模式
--prefix为前缀,可由自己随便添加,为了方便识别函数
server.xml为接口描述文件
server_glue.h为生成的绑定文件
下面为截取部分绑定文件内容: 注意这部分可能被重复定义,不能多次包含这个头文件server_glue.h
#include <dbus/dbus-glib.h>
static const DBusGMethodInfo dbus_glib_com_zhang_methods[] = {
{ (GCallback) com_zhang_add, dbus_glib_marshal_com_zhang_BOOLEAN__INT_INT_POINTER_POINTER, 0 },
{ (GCallback) com_zhang_sub, dbus_glib_marshal_com_zhang_BOOLEAN__UINT_UINT_POINTER_POINTER, 65 },
}; /*生成了两个回调函数com_zhang_add,com_zhang_sub*/
const DBusGObjectInfo dbus_glib_com_zhang_object_info = {
0,
dbus_glib_com_zhang_methods,
2,
"org.zhang.Test.Basic/0Add/0S/0input0/0I/0i/0input1/0I/0i/0output/0O/0F/0N/0i/0/0org.zhang.Test.Basic/0Sub/0S/0arg0/0I/0u/0arg1/0I/0u/0ret/0O/0F/0N/0u/0/0/0", /*对应.xml的两个接口*/
"org.zhang.Test.Basic/0Hello/0org.zhang.Test.Basic/0Coordinate/0/0", /*对应.xml的两个信号*/
"/0"
};
设对象前缀是$(prefix),则生成的DBusGObjectInfo结构变量名就是 dbus_glib_$(prefix)_object_info。绑定文件会为接口方法定义回调函数,如上面的(com_zhang_add和com_zhang_sub)。回调函数的名称是这样的:首先将xml中的方法名 称转换到全部小写,下划线分隔的格式,然后增加前缀"$(prefix)_"。例如:如果xml中有方法Add,绑定文件就会引用一个名 称为$(prefix)_add的函数。
绑定文件还会为接口方法生成用于散集(Unmarshaling)的函数。在dbus消息中,方法参数是以流格式存在的。该函数将方法参数由数据流还原到glib的数据格式,并传入方法的回调函数。
在包含绑定文件前,我们必须声明绑定文件要引用的回调函数。即在包含server_glue.h前,必须先声明com_zhang_add和com_zhang_sub函数,这个很重要。
3、对象
dbus-glib用GObject实现dbus对象。所以我们首先要实现一个对象。
3.1 对象定义
先写个对象头文件com_zhang_myobject.h
com_zhang_myobject.h文件内容如下:
#include<glib-object.h> /*这个一定要记住添加,不然会出现找不到GObject and GObjectClass*/
typedef struct ComZhangMyObject ComZhangMyObject;
typedef struct ComZhangMyObjectClass ComZhangMyObjectClass;
GType com_zhang_myobject_get_type(void);
struct ComZhangMyObject
{
GObject parent;
};
struct ComZhangMyObjectClass
{
GObjectClass parent;
};
gboolean com_zhang_add(ComZhangMyObject *obj,gint IN_input0, gint IN_input1,gint *OUT_output,GError **error);
/*回调函数声明,名字必须与生成的绑定文件中的回调函数名字相同*/
gboolean com_zhang_sub(ComZhangMyObject *obj,gint IN_arg0,gint IN_arg1,gint *OUT_ret,GError **error);
/*回调函数声明*/
void com_zhang_hello(ComZhangMyObject obj,const char siglal);
/*自己定义处理信号的函数,名字没特别要求*/
void com_zhang_coodinate(ComZhangMyObject *obj,gint coodinate0,gint coodinate1);
/*自己定义处理信号的函数,名字没特别要求*/
G_DEFINE_TYPE(ComZhangMyObject,com_zhang_myobject,G_TYPE_OBJECT)
Gobject的对象定义虽然繁琐,但有固定的套路。依样画葫芦,画多了就习惯了。我们在com_zhang_myobject.h中声明了com_zhang_add和com_zhang_sub函数。 这两个函数将是在com_zhang_myobject.c中实现的,它们在绑定文件server_glue.h中用到。因为主程序要使用绑定文件中的对象信息,所以应由主程序包含绑定文件。主程序只要在包含绑定文件前包含com_zhang_myobject.h,编译器就不会抱怨com_zhang_add和com_zhang_sub 函数未声明了。
3.2 信号的列集函数实现
列集(Marshaling)是将数据从某种格式存为流格式的操作;散集(Unmarshaling)则是列集的反操作,将信息从流格式中还原出 来。在绑定文件中,dbus-binding-tool自动生成函数将方法参数从dbus消息中还原出来,即实现了散集。那么我们怎么把信号参数由 glib的数据结构转换到消息中的数据流呢?
因为ComZhangMyObject对象有信号存在,所以在对象类初始化函数com_zhang_myobject_class_init中,我们要调用g_signal_new创建信号。 g_signal_new要求我们提供一个列集函数。
因为信号Coordinate需要发送两个G_TYPE_INT的整数给应用程序,如果传递的参数只有一个,那么只要在gmarshal.h中找到该列集函数就可以了,如果发送信号的参数比较复杂,那么则需要自己生成列集函数。
glib有一些标准的列集函数,在gmarshal.h中定义。例如g_cclosure_marshal_VOID__STRING,这个函数适合只有 一个字符串参数的信号。如果gmarshal.h没有提供适合的列集函数,我们可以用一个叫glib-genmarshal的工具自动生成列集函数。后面 我们会看到,无论是标准列集函数还是生成的列集函数都是既可以用于列集也可以用于散集,这些函数通常都被称作列集函数。
定义列集文件server_marshal.list
$cat server_marshal.list
VOID:INT,INT
其中第一个为返回值,第二、三个为传递的参数类型
各种类型可以如下所示:
# VOID indicates no return type, or no extra
# parameters. if VOID is used as the parameter
# list, no additional parameters may be present.
# BOOLEAN for boolean types (gboolean)
# CHAR for signed char types (gchar)
# UCHAR for unsigned char types (guchar)
# INT for signed integer types (gint)
# UINT for unsigned integer types (guint)
# LONG for signed long integer types (glong)
# ULONG for unsigned long integer types (gulong)
# ENUM for enumeration types (gint)
# FLAGS for flag enumeration types (guint)
# FLOAT for single-precision float types (gfloat)
# DOUBLE for double-precision float types (gdouble)
# STRING for string types (gchar*)
# PARAM for GParamSpec or derived types (GParamSpec*)
# BOXED for boxed (anonymous but reference counted) types (GBoxed*)
# POINTER for anonymous pointer types (gpointer)
# OBJECT for GObject or derived types (GObject*)
# NONE deprecated alias for VOID
# BOOL deprecated alias for BOOLEAN
我们使用glib-genmarshal 生成列集函数
调用命令glib-genmarshal --header --prefix=com_zhang server_marshal.list >server_marshal.h
glib-genmarshal --body --prefix=com_zhang server_marshal.list >server_marshal.c
其中—header用于生成.h文件,--body用于生成.c文件。--prefix的功能和上面相同。在server_marshal.h中生成了$(prefix)_VOID__INT_INT()函数。在$(prefix)_VOID__INT_INT()void是信号的返回类型,INT是信号传出的参数类型。
在server_marshal.c中注意包含server_marshal.h的头文件。
3.3 对象的实现
首先看下g_signal_new()函数
guint g_signal_new (const gchar *signal_name,
GType itype,
GSignalFlags signal_flags,
guint class_offset,
GSignalAccumulator accumulator,
gpointer accu_data,
GSignalCMarshaller c_marshaller,
GType return_type,
guint n_params,
...)
对象的实现文件com_zhang_myobject.c
cat com_zhang_myobject.c
#include"com_zhang_myobject.h"
#include"server_marshal.h" /*包含它是因为用到列集函数g_cclosure_marshal_VOID__STRING和com_zhang_VOID__INT_INT,
G_DEFINE_TYPE(ComZhangMyObject,com_zhang_myobject,G_TYPE_OBJECT)
enum
{
HELLO_MESSAGE,
/*Hello 信号*/
Coordinate_MESSAGE,
/*Coordinate信号*/
LAST_SIGNAL
/*这个应该是用来定义信号数组的大小的*/
};
static guint signals[LAST_SIGNAL];
static void com_zhang_myobject_init(ComZhangMyObject *obj)
{
}
static void com_zhang_myobject_class_init(ComZhangMyObjectClass *kclass)
{
/*一般信号在这里初始化,有多少个信号,则初始化多少个*/
signals[HELLO_MESSAGE]=g_signal_new("hello",
/*名字*/
G_OBJECT_CLASS_TYPE(kclass),
G_SIGNAL_RUN_LAST|G_SIGNAL_DETAILED,
0,
NULL,NULL,
信号收集器,和传递的数据
g_cclosure_marshal_VOID__STRING,
列集函数
G_TYPE_NONE,
/*返回值*/
1,
传递的参数个数
G_TYPE_STRING);
传递的参数类型,有几个则写几个
signals[Coordinate_MESSAGE]=g_signal_new("coordinate",
G_OBJECT_CLASS_TYPE(kclass),
G_SIGNAL_RUN_LAST|G_SIGNAL_DETAILED,
0,
NULL,NULL,
com_zhang_VOID__INT_INT,
G_TYPE_NONE,
/*返回值*/
2,
传递的参数个数
G_TYPE_INT,G_TYPE_INT);
传递的参数类型,有几个则写几个
}
gboolean com_zhang_add(ComZhangMyObject *object,gint input0,gint input1,gint *output,GError **error)
/*回调函数,当客户端请求Add方法是,则调用这个回调函数,intpu1 和 intput0是客户端传过来的参数,经处理后得到的*output会传回给客户端,如果发生错误,则会把错误传过去*/
{
g_print("The input parmeters are %d and %d/n",input0,input1);
*output=input0+input1;
g_print("The output is %d/n",*output);
return TRUE;
}
gboolean com_zhang_sub(ComZhangMyObject *object,gint IN_arg0,gint IN_arg1,gint *OUT_ret,GError **error)
/*回调函数,当客户端请求sub方法是,则调用这个回调函数*/
{
*OUT_ret=IN_arg0-IN_arg1;
return TRUE;
}
void com_zhang_hello(ComZhangMyObject *obj,const char *signal_hello)
{
/*发送Hello信号,发送一个(char *signal_hello)字符串给客户端,在主函数server.c中调用这个函数发送信号*/
g_signal_emit(obj,signals[HELLO_MESSAGE],0,signal_hello);
}
void com_zhang_coordinate(ComZhangMyObject *obj,gint coordinate0,gint coordinate1)
{
/*发送Coordinate信号,发送两个gint数(coordinate0和coordinate1)给客户端,在主函数server.c中调用这个函数发送信号*/
g_signal_emit(obj,signals[Coordinate_MESSAGE],0,coordinate0,coordinate1);
}
Google g_signal_emit函数
g_signal_emit(对象,初始化好的信号,返回值,发出参数1,发出参数2);
3.4 主程序server.c
$cat server.c
#include<stdio.h>
#include<stdlib.h>
#include<dbus/dbus.h>
#include<dbus/dbus-glib.h>
#include<glib/giochannel.h>
#include<string.h>
#include"com_zhang_myobject.h"
#include"server_glue.h"
/*
为了增加一个敲键测试信号发射。但又必须在glib主循环里等待dbus的消息。怎样才能既等待dbus消息,又等待敲键呢?这种情况可以使用 glib的IOChannels。glib的IOChannels允许我们在glib主循环等待指定的文件或socket句柄。
*/
static gboolean channel_fun(GIOChannel *source,GIOCondition condition,gpointer data)
{
/*这是IOChannel的一个回调函数,因为进入mainloop主循环中,我们需要打断它来发送信号,这个IOChannel能监听键盘的输入*/
gint rc;
char buf[1024];
ComZhangMyObject *obj=(ComZhangMyObject *)data;
if(condition !=G_IO_IN){
return TRUE;
}
/*ve've received something on stdin */
g_print("************************/n");
rc=fscanf(stdin,"%s",buf);
if(rc<=0){
g_print("NULL/n");
return TRUE;
}
if(!strcmp(buf,"send1")){
/*发送Hello 信号给监听这个服务的客户端,而不是发送给所有的客户端,在客户端那里会指定客户端只感兴趣的服务。这里发送一个“Hello World”字符串给客户端*/
com_zhang_hello(obj,"Hello World");
g_print("send signal Hello World/n");
}else if(!strcmp(buf,"send2")){
com_zhang_coordinate(obj,8,9);
/*发送两个gint数给客户端*/
g_print("send signal two number: 8 and 9/n");
}else{
g_print("Unknown command '%s' /n",buf);
}
return TRUE;
}
gint main(gint argc,gchar **argv)
{
DBusGConnection *connection;
GMainLoop *mainloop=NULL;
ComZhangMyObject *obj;
GError *error=NULL;
DBusGProxy *proxy;
GIOChannel *chan;
guint ret;/*想/org/freedesktop/DBus申请名字时的返回值*/
g_type_init();
/*初始化*/
mainloop=g_main_loop_new(NULL,FALSE);
dbus_g_object_type_install_info(COM_ZHANG_MYOBJECT_TYPE,&dbus_glib_com_zhang_object_info);
/*调用dbus_g_object_type_install_info登记ComZhangMyObject类的接口信息*/
connection=dbus_g_bus_get(DBUS_BUS_SESSION,&error);/*连接到session bus,并判断是否连接成功,如果是要接收来自系统的消息,则可改为DBUS_BUS_SYSTEM*/
if(connection==NULL){
g_printerr("Failed to connect to D-Bus session : %s/n",error->message);
g_error_free(error);
return 1;
}
/*创建一个代理,并为这个服务申请一个名字,名字为com.zhang.test. /org/freedesktop/DBus是已经存在好了的服务,调用接口RequestName可以申请一个well-know name, bus-daemon会为你分配一个唯一的名字,类似于:1.22 */
proxy=dbus_g_proxy_new_for_name(connection,"org.freedesktop.DBus","/org/freedesktop/DBus",
"org.freedesktop.DBus");
if(!dbus_g_proxy_call(proxy,"RequestName",&error,G_TYPE_STRING,"com.zhang.test",G_TYPE_UINT,0,G_TYPE_INVALID,G_TYPE_UINT,&ret,G_TYPE_INVALID)){
g_printerr("Request Name error: %s/n",error->message);
g_error_free(error);
return 1;
}
obj=g_object_new(COM_ZHANG_MYOBJECT_TYPE,NULL);
/*创建一个新的对象*/
dbus_g_connection_register_g_object(connection,"/com/zhang/Myobject",G_OBJECT(obj));
/*登记对象,对象路径为/com/zhang/Myobject,并传递一个对象指针*/
chan=g_io_channel_unix_new(0);
/*创建一个GIOChannel*/
g_io_add_watch(chan,G_IO_IN,channel_fun,obj);
/*增加一个监视函数channel_fun*/
g_print("zhang fang run/n");
g_main_loop_run(mainloop);
/*进入主循环,等待客户端发送方法调用*/
return 0;
}
错误
错误一:
server_glue.h:164: error: ‘com_zhang_add’ undeclared here (not in a function)
server_glue.h:165: error: ‘com_zhang_sub’ undeclared here (not in a function)
去掉#include"server_glue.h",因为com_zhang_add和com_zhang_sub在com_zhang_myobject.h已经声明,在servre_glue.h中的只是回调函数。
错误二:
com_zhang_myobject.c:31: error: ‘com_zhang_VOID__INT_INT’ undeclared (first use in this function)
在com_zhang_myobject.c中添加server_marshal.h文件,因为在信号初始化是调用的列集函数是在server_marshal.h中定义的。
D-Bus实例分析(服务端)
最新推荐文章于 2021-05-10 18:04:44 发布