dbus-glib应用入门

家住魔仙堡 提交于 2019-12-06 08:16:16

操作系统: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执行文件)

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!