操作系统:ubuntu16.04
需要安装的依赖包:sudo apt install libglib2.0-dev libdbus-glib-1-dev libdbus-1-dev
工作中有用到dbus-glib编程接口,官网文档已经不建议使用,但是还是简单小结一下,我是一个初级入门者,如果有问题还希望多多指教。
一、几个需要明确的概念:
1、Dbus概念
Dbus是一个进程间的通信机制,可以是应用与应用之间的通信,也可以是应用与系统之间的通信。
2、Dbus Daemon
在DBus的体系中,有一个常驻的进程 Daemon,所有进程间的交互都通过它来进行分发和管理。所有希望使用 DBus 进行通信的进程,都必须事先连上 Daemon,并将自己的名字注册到 Daemon 上,之后,Daemon会根据需要把消息以及数据发到相应的进程中。
3、通信机制
DBus 提供的最简单的一种通信方式是信号(Signal),应用程序可以发送一个信号到 Daemon 上,之后,Daemon 会根据信号的种类和谁希望得到信号等信息,把相应的数据发给每个希望得到信号的进程。
4、常用到各种名字
(1)DBUS name
在 DBus 中最为重要的名字是“Bus Name”,Bus Name 是一个每个应用程序(或是通信对象)用来标识自己用的。几乎可以当成是“IP”地址来理解。Bus Name有两种,一种是“Unique Connection Name”,是以冒号开头的,是全局唯一但“人类不友好”的命名,一种是“Well-know Name”,人类友好的。Bus Name 的命名规则是:
- Bus name 就像网址一样,由“.”号分割的多个子字符串组成,每个子字符串都必须至少有一个以上的字符。
- 每个子字符串都只能由“[A-Z][a-z][0-9]_-”这些 ASCII 字符组成,只有 Unique Name 的子串可以以数字开头。
- 每个 Bus name 至少要有一个“.”,和两个子字符串,不能以“.”开头
- Bus name 不能超过 255 个字符
(2)Interface name
DBus 也有 interface 这个概念,主要是用来为更高一层的框架使用方面而设定的。在 C API 这一层,你几乎可以无视这个概念,只需要知道这个一个“字符串”,并在消息匹配是被 DBus 使用到,会随着消息在不同的进程之前传递,从进程 A 发送一个消息或是数据到进程 B 时,其中必定会带有一个部分就是这个字符串,至于 B 进程怎么用(或是无视它)都可以。它的命名规则与 DBus Name 几乎是一样的,只有一点要注意,interface name 中不能带有“-”字符。
(3)Object path
DBus 中的 object path,与 interface 一样,也只是个概念在更高一层的框架(QT Dbus)中才比较有用,在 C API 这一层,几乎可以无视这个概念,把它当成一个普通的字符串,根据通信的需要,用来做一种标识和区分。Object path 的命名规则是:/com/example/MusicPlayer1
- object path 可以是任意长度的
- 以'/'开头,并以以'/'分隔的若干子字符串组成
- 每个子串必须由“[A-Z][a-z][0-9]_”中的字符组成
- 不能有空子串(也就是不能连续两个'/'符)
- 除了“root path”('/')之外,不能再有 object path 是以 '/' 结尾的了。
(4)Member name
Member 包含两种类型,一种是 Signal,一种是 Method。在大多数方面,他们几乎是一样的,除了两点:1. Signal是在总线中进行广播的,而Method是指定发给某个进程的。2. Signal 不会有返回,而 Method 一定会有返回(同步的或是异步的)。Member name的命名规则是这样的:
- 只能包含"[A-Z][a-z][0-9]_"这些字符,且不能以数字开头。不能包含“.”。
- 不能超过255个字符
二、编程实现(基于signal)
前期准备:
新建一个mydbus的文件夹
在mydbus目录下:
- 新建configure.ac和Makefile.am文件以实现makefile的自动编译;
- 新建src目录,目录下有Makefile.am文件,client.c文件,server.c文件及signal.xml配置文件。
具体编程如下:
1、 server.c文件
#include <stdio.h>
#include <dbus/dbus-glib.h>
#include <stdlib.h>
static void lose (const char *fmt, ...) G_GNUC_NORETURN G_GNUC_PRINTF (1, 2);
static void lose_gerror (const char *prefix, GError *error) G_GNUC_NORETURN;
static void lose (const char *str, ...)
{
va_list args;
va_start (args, str);
vfprintf (stderr, str, args);
fputc ('\n', stderr);
va_end (args);
exit (1);
}
static void lose_gerror (const char *prefix, GError *error)
{
lose ("%s: %s", prefix, error->message);
}
static gboolean emit_signal (gpointer arg)
{
DBusGProxy *proxy = arg;
dbus_g_proxy_call_no_reply (proxy, "emitHelloSignal", G_TYPE_INVALID);
return TRUE;
}
static void hello_signal_handler (DBusGProxy *proxy, const char *hello_string, gpointer user_data)
{
printf ("Received signal and it says: %s\n", hello_string);
}
int main()
{
DBusGConnection *bus;
DBusGProxy *remote_object;
GError *error = NULL;
GMainLoop *mainloop;
//GType初始化
g_type_init();
//申请创建主循环
mainloop = g_main_loop_new(NULL, FALSE);
//和SESSION BUS建立连接,也可以通过DBUS_BUS_SYSTEM与系统总线连接
bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
if (!bus)
lose_gerror ("Couldn't connect to session bus", error);
//获取一个对象代理
remote_object = dbus_g_proxy_new_for_name (bus,
"org.designfu.TestService", //server
"/org/designfu/TestService/object", //path
"org.designfu.TestService"); //interface
if (!remote_object) lose_gerror ("Failed to get name owner", error);
//增加一个信号,通知对象调用者需要捕获指定信号
dbus_g_proxy_add_signal (remote_object, "HelloSignal", G_TYPE_STRING, G_TYPE_INVALID);
//连接信号,将处理函数handler连接到指定的信号上。
dbus_g_proxy_connect_signal (remote_object, "HelloSignal", G_CALLBACK (hello_signal_handler),NULL, NULL);
//进入到主循环中
g_main_loop_run(mainloop);
exit(0);
}
2、client.c文件
#include <dbus/dbus-glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <glib/giochannel.h>
static void lose (const char *fmt, ...) G_GNUC_NORETURN G_GNUC_PRINTF (1, 2);
static void lose_gerror (const char *prefix, GError *error) G_GNUC_NORETURN;
static void lose (const char *str, ...)
{
va_list args;
va_start (args, str);
vfprintf (stderr, str, args);
fputc ('\n', stderr);
va_end (args);
exit (1);
}
static void lose_gerror (const char *prefix, GError *error)
{
lose ("%s: %s", prefix, error->message);
}
typedef struct TestObject TestObject;
typedef struct TestObjectClass TestObjectClass;
GType test_object_get_type (void);
struct TestObject
{
GObject parent;
};
struct TestObjectClass
{
GObjectClass parent;
};
enum
{
HELLO_SIGNAL,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
#define TEST_TYPE_OBJECT (test_object_get_type ())
//#define TEST_OBJECT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), TEST_TYPE_OBJECT, TestObject))
//#define TEST_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TEST_TYPE_OBJECT, TestObjectClass))
//#define TEST_IS_OBJECT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), TEST_TYPE_OBJECT))
//#define TEST_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TEST_TYPE_OBJECT))
//#define TEST_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_OBJECT, TestObjectClass))
G_DEFINE_TYPE(TestObject, test_object, G_TYPE_OBJECT);
gboolean test_object_emit_hello_signal (TestObject *obj, GError **error);
#include "signal-glue.h"
static void test_object_init (TestObject *obj)
{
}
static void test_object_class_init (TestObjectClass *klass)
{
signals[HELLO_SIGNAL] =
g_signal_new ("hello_signal",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
0,
NULL, NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE, 1, G_TYPE_STRING);
}
gboolean test_object_emit_hello_signal (TestObject *obj, GError **error)
{
g_signal_emit (obj, signals[HELLO_SIGNAL], 0, "Hello");
return TRUE;
}
static void shell_help(void)
{
printf( "\ts\tsend signal\n"
"\tq\tQuit\n"
);
}
#define STDIN_BUF_SIZE 1024
static gboolean channel_cb(GIOChannel *source, GIOCondition condition, gpointer data)
{
int rc;
char buf[STDIN_BUF_SIZE+1];
TestObject *obj = (TestObject *)data;
if (condition != G_IO_IN) {
return TRUE;
}
/* we've received something on stdin. */
printf("# ");
rc = fscanf(stdin, "%s", buf);
if (rc <= 0) {
printf("NULL\n");
return TRUE;
}
if (!strcmp(buf, "h")) {
shell_help();
} else if (!strcmp(buf, "?")) {
shell_help();
} else if (!strcmp(buf, "s")) {
g_signal_emit (obj, signals[HELLO_SIGNAL], 0, "Hello");
} else if (!strcmp(buf, "q")) {
exit(0);
} else {
printf("Unknown command `%s'\n", buf);
}
return TRUE;
}
int main()
{
DBusGConnection *bus;
DBusGProxy *bus_proxy;
GError *error = NULL;
TestObject *obj;
GMainLoop *mainloop;
guint request_name_result;
GIOChannel *chan;
guint source;
//GType初始化
g_type_init();
//对象的安装信息结构告诉DBUS
dbus_g_object_type_install_info (TEST_TYPE_OBJECT, &dbus_glib_test_object_object_info);
//申请创建主循环
mainloop = g_main_loop_new (NULL, FALSE);
//和SESSION BUS建立连接,也可以通过DBUS_BUS_SYSTEM与系统总线连接
bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
if (!bus)
lose_gerror ("Couldn't connect to session bus", error);
//获取一个对象代理
bus_proxy = dbus_g_proxy_new_for_name (bus,
"org.freedesktop.DBus", //server
"/org/freedesktop/DBus", //path
"org.freedesktop.DBus"); //interface
//同步调用对象的方法并返回结果,参数error后面是输入参数列表和输出参数列表。
if (!dbus_g_proxy_call (bus_proxy, "RequestName", &error,
G_TYPE_STRING, "org.designfu.TestService",
G_TYPE_UINT, 0,
G_TYPE_INVALID,
G_TYPE_UINT, &request_name_result,
G_TYPE_INVALID))
lose_gerror ("Failed to acquire org.designfu.TestService", error);
//创建一个新对象
obj = g_object_new (TEST_TYPE_OBJECT, NULL);
//注册对象,向Dbus的一个连接注册对象
dbus_g_connection_register_g_object (bus, "/org/designfu/TestService/object", G_OBJECT (obj));
printf ("test service running\n");
//创建iochannel
chan = g_io_channel_unix_new(0);
//把你所需要处理的发生文件描述符上的事件加到事件循环中
source = g_io_add_watch(chan, G_IO_IN, channel_cb, obj);
//开始进入主循环
g_main_loop_run (mainloop);
exit(0);
}
3、signal.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<node name="/">
<interface name="org.designfu.TestService">
<method name="emitHelloSignal">
</method>
<!-- Mark the signal as exported -->
<signal name="HelloSignal"/>
</interface>
</node>
4、src/Makefile.am
AM_CPPFLAGS = \
$(DBUS_CFLAGS) \
$(DBUS_GLIB_CFLAGS)
LIBS = \
$(DBUS_LIBS) \
$(DBUS_GLIB_LIBS) \
-ldbus-glib-1
# example-signal-emitter
noinst_PROGRAMS = client
client_SOURCES = signal-glue.h client.c
BUILT_SOURCES = signal-glue.h
signal-glue.h: signal.xml
$(LIBTOOL) --mode=execute dbus-binding-tool --prefix=test_object --mode=glib-server --output=signal-glue.h $(srcdir)/signal.xml
CLEANFILES = $(BUILT_SOURCES)
EXTRA_DIST = signal.xml
# example-signal-recipient
noinst_PROGRAMS += server
server_SOURCES= server.c
5、Makefile.am
SUBDIRS = src
6、运行步骤:
$aclocal ----->生成aclocal.m4处理本地宏文件
$autoheader -------->生成config.h.in
$autoconf --------->生成configure.ac和aclocal.m4
$automake --add-missing ----------->生成configure
$./configure --------->生成makefile文件
$make ---------->生成二进制执行文件(src下server和client执行文件)
来源:CSDN
作者:Ada-Lee
链接:https://blog.csdn.net/jjcat8/article/details/73565109