DBUS 实现c/s 全双工

时间:2023-01-17 20:40:02

前一篇文章主要介绍了dbus调用的流程,及简单的单工通信,这里记录下双工通信的流程,供后续参考。

定义dbus名称,路径,接口,方法等。

额外注意点:

1. 所有的全局资源必须加锁

2. handle-method-call中 从g_variant_get获得的字符串,不需要自己释放,系统会自动释放,注意如果后续需要使用,必须strdump出来。

 

#define TEST_DBUS_A "methodA"
#define TEST_DBUS_B "methodB"
#define TEST_DBUS_INTERFACE "com.test.hello"
#define TEST_DBUS_OBJPATH "/com/test/hello"
#define TEST_DBUS_NAME "com.test.hello"

#define TEST_DBUS_TIMEOUT 100000


 

 


client

static GDBusConnection *test_client_conn = NULL;
static GDBusNodeInfo *dbus_node_info = NULL;
static pthread_mutex_t callback_info_list_mutex;
GHashTable *g_callback_info_list = NULL;
typedef struct {
	void *callback;
	void *user_data;
} dbus_client_data;

static void __handle_method_call(GDBusConnection *conn, const gchar *sender,
		const gchar *obj_path, const gchar *iface, const gchar *method_name,
		GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data)
{
	printf("sender[%s],obj_path[%s],iface[%s],method_name[%s]", sender,obj_path,iface,method_name);
	void* callback_addr = NULL;
	void* ud = NULL;
	dbus_client_data *dcd = NULL;
	pthread_mutex_lock(&callback_info_list_mutex);
	dcd = g_hash_table_lookup(g_callback_info_list, method_name);
	g_hash_table_remove(g_callback_info_list, dcd);
	pthread_mutex_unlock(&callback_info_list_mutex);
	if (dcd) {
		callback_addr = dcd->callback;
		ud = dcd->user_data;
	}
	if (0 == g_strcmp0(method_name, TEST_DBUS_A)) {

		int ret = 0;
		char *string1 = NULL;
		if (parameters) {
			g_variant_get(parameters, "(&s)", &string1);
		}
		if (callback_addr != NULL) {
			((cb)callback_addr)(string1, ud);
		} else {
			printf("callback address is NULL");
		}

		g_dbus_method_invocation_return_value(invocation, g_variant_new ("(i)", ret));


	}   else {
		g_dbus_method_invocation_return_error(invocation,
			G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD,
			"Method %s is not implemented on interface %s", method_name, iface);
	}
	if (parameters) {
		g_variant_unref(parameters);
	}
	g_free(dcd);
	
}

int _set_cb(void* callback, void *user_data)
{
	
	
	dbus_client_data *dcd = calloc(1, sizeof(dbus_client_data));
	dcd->callback = callback;
	dcd->user_data = user_data;
	pthread_mutex_lock(&callback_info_list_mutex);
	g_hash_table_insert(g_callback_info_list, TEST_DBUS_A, dcd);
	pthread_mutex_unlock(&callback_info_list_mutex);

	return ret;
}

int __trigger_methodB()
{
	GError *gerr = NULL;

	g_dbus_connection_call_sync(test_client_conn,
				TEST_DBUS_NAME, TEST_DBUS_OBJPATH, TEST_DBUS_INTERFACE,
				TEST_DBUS_B,
				g_variant_new("(is)", 1, "hello"),
				NULL,
				G_DBUS_CALL_FLAGS_NONE,
				TEST_DBUS_TIMEOUT,
				NULL,
				&gerr);
	if (gerr) {
		printf("g_dbus_connection_call_sync() Fail(%s)", gerr->message);
		g_error_free (gerr);
		return false;
	}
	return 0;
}
bool _test_dbus_client_init()
{
	if (test_client_conn) {
		return true;
	}

	GError *gerr = NULL;
	const gchar introspection_xml[] =
	"<node>"
	"	<interface name='"TEST_DBUS_INTERFACE"'>"
	"		<method name='"TEST_DBUS_A"'>"
	"			<arg type='s' name='data' direction='in'/>"
	"			<arg type='i' name='ret' direction='out'/>"
	"		</method>"
    "	</interface>"
	"</node>";

	dbus_node_info = g_dbus_node_info_new_for_xml(introspection_xml, NULL);
	if (dbus_node_info == NULL) {
		printf("Initialization failed");
		return false;
	}
	gchar *addr = g_dbus_address_get_for_bus_sync(G_BUS_TYPE_SESSION, NULL, &gerr);
	if (gerr) {
		printf("Getting address failed (%s)", gerr->message);
		g_error_free(gerr);
		return false;
	}

	test_client_conn = g_dbus_connection_new_for_address_sync(addr,
			(GDBusConnectionFlags)(G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT | G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION),
			NULL, NULL, &gerr);
	g_free(addr);
	if (gerr) {
		printf("Connection failed (%s)", gerr->message);
		g_error_free(gerr);
		return false;
	}

	GDBusInterfaceVTable vtable;
	vtable.method_call = __handle_method_call;
	vtable.get_property = NULL;
	vtable.set_property = NULL;

	guint reg_id = -1;
	reg_id = g_dbus_connection_register_object(test_client_conn, TEST_DBUS_OBJPATH,
			dbus_node_info->interfaces[0], &vtable, NULL, NULL, &gerr);
	if (gerr) {
		printf("g_dbus_connection_register_object() Fail(%s)", gerr->message);
		g_error_free(gerr);
		return false;
	}
	printf("reg_id[%d]", reg_id);
	if (reg_id == 0) {
		printf("Failed to g_dbus_connection_register_object");
		return false;
	}
	printf("Dbus connection established: %s", g_dbus_connection_get_unique_name(test_client_conn));
	g_callback_info_list = g_hash_table_new(g_str_hash, g_str_equal);

	pthread_mutex_init(&callback_info_list_mutex, NULL);

	return true;
}

void _test_dbus_client_deinit(void)
{
	
	if (test_client_conn) {
		g_dbus_connection_flush_sync(test_client_conn, NULL, NULL);
		g_dbus_connection_close_sync(test_client_conn, NULL, NULL);
		g_object_unref(test_client_conn);
		test_client_conn = NULL;
	}
	if (g_callback_info_list) {
		g_hash_table_destroy(g_callback_info_list);
		g_callback_info_list = NULL;
	}

	if (dbus_node_info) {
		g_dbus_node_info_unref(dbus_node_info);
		dbus_node_info = NULL;
	}

	pthread_mutex_destroy(&callback_info_list_mutex);
	
}

 

server

static pthread_mutex_t callback_list_mutex;
GHashTable *g_sender_info_list = NULL;

static GDBusConnection *test_dbus_conn = NULL;
GHashTable *g_callback_list = NULL;

static void __dbus_handle_method_call(GDBusConnection *connection,
		const gchar *sender,
		const gchar *object_path,
		const gchar *interface_name,
		const gchar *method_name,
		GVariant *parameters,
		GDBusMethodInvocation *invocation,
		gpointer user_data)
{
	printf("sender[%s],object_path[%s],interface_name[%s],method_name[%s]", sender,object_path,interface_name,method_name);
	if (0 == g_strcmp0(method_name, TEST_DBUS_B)) {
		int res = 0;
		char *data_str = NULL;
		if (parameters) {
			g_variant_get(parameters, "(i&s)", res, &data_str);
			printf("res[%d], data_str[%s]", res, data_str);
		}
		g_free(data_str);
		g_dbus_method_invocation_return_value(invocation, g_variant_new ("(i)", ret));
	}
	else {
		g_dbus_method_invocation_return_error(invocation,
			G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD,
			"Method %s is not implemented on interface %s", method_name, interface_name);
	}
	if (parameters) {
		g_variant_unref(parameters);
	}
}


static const GDBusInterfaceVTable interface_vtable =
{
	__dbus_handle_method_call,
	NULL,
	NULL
};


static void __dbus_on_bus_acquired(GDBusConnection *conn, const gchar *name,
		gpointer user_data)
{
	guint registration_id = 0;
	GError *error = NULL;
	GDBusNodeInfo *introspection_data = (GDBusNodeInfo *)user_data;

	test_dbus_conn = conn;

	registration_id = g_dbus_connection_register_object(conn,
		TEST_DBUS_OBJPATH,
		introspection_data->interfaces[0],
		&interface_vtable,
		NULL,/* user_data */
		NULL,/* user_data_free_func */
		&error);

	if (0 == registration_id) {
		printf("g_dbus_connection_register_object() Fail(%s)", error->message);
		g_error_free(error);
	}

	printf("g_dbus_connection_register_object() succeed");
}

static void __dbus_on_name_lost(GDBusConnection *connection, const gchar *name,
		gpointer user_data)
{
	printf("Lost the name %s", name);
}

static void __dbus_on_name_acquired(GDBusConnection *connection, const gchar *name,
		gpointer user_data)
{
	printf("Acquired the name %s", name);
}





int _test_dbus_server_trigger_methodA(char *sender, char *data)
{

	GError *error = NULL;
	GVariant *result;

	result = g_dbus_connection_call_sync(test_dbus_conn,
				sender, TEST_DBUS_OBJPATH, TEST_DBUS_INTERFACE,
				TEST_DBUS_A,
				g_variant_new("(s)", data),
				NULL,
				G_DBUS_CALL_FLAGS_NONE,
				TEST_DBUS_TIMEOUT,
				NULL,
				&error);

	if (error) {
		printf("g_dbus_connection_call_sync() Fail(%s)", error->message);
		ret = SAMSUNG_CLOUD_ERROR_UNKNOWN;
		goto FINISH_OFF;
	}

	if (!result) {
		printf ("g_dbus_connection_call_sync 'Launch' error");
		ret = SAMSUNG_CLOUD_ERROR_UNKNOWN;
		goto FINISH_OFF;
	}

	g_variant_get(result, "(i)", &ret);
	printf("ret[%d]", ret);

FINISH_OFF:
	if (error) {
		g_error_free (error);
	}

	if (result) {
		g_variant_unref(result);
	}
	
	return ret;
}

static void __hash_table_free_cb(gpointer data)
{
	g_free(data);
}

unsigned int _test_dbus_server_init(CB cb)
{
	guint id;
	GError *error = NULL;
	GDBusNodeInfo *introspection_data = NULL;

	const gchar introspection_xml[] =
	"<node>"
	"	<interface name='"TEST_DBUS_INTERFACE"'>"
	"		<method name='"TEST_DBUS_B"'>"
	"			<arg type='i' name='res' direction='in'/>"
	"			<arg type='s' name='data' direction='in'/>"
	"			<arg type='i' name='ret' direction='out'/>"
	"		</method>"
    "	</interface>"
	"</node>";


	introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, &error);
	if (NULL == introspection_data) {
		printf("g_dbus_node_info_new_for_xml() Fail(%s)", error->message);
		g_error_free(error);
		return -1;
	}

	id = g_bus_own_name(G_BUS_TYPE_SESSION,
			TEST_DBUS_NAME,
			G_BUS_NAME_OWNER_FLAGS_REPLACE,
			__dbus_on_bus_acquired,
			__dbus_on_name_acquired,
			__dbus_on_name_lost,
			introspection_data,
			(GDestroyNotify)g_dbus_node_info_unref);
	if (0 == id) {
		printf("g_bus_own_name() Fail");
		return 0;
	}
	g_sender_info_list = g_hash_table_new_full(g_str_hash, g_str_equal, __hash_table_free_cb, __hash_table_free_cb);
	g_callback_list = g_hash_table_new(g_str_hash, g_str_equal);
	pthread_mutex_init(&callback_list_mutex, NULL);
	pthread_mutex_lock(&callback_list_mutex);
	g_hash_table_insert(g_callback_list, TEST_DBUS_C, cb);
	pthread_mutex_unlock(&callback_list_mutex);

	return id;
}

void _sc_dbus_server_deinit(unsigned int id)
{
	g_bus_unown_name(id);
	if (g_callback_list) {
		g_hash_table_destroy(g_callback_list);
		g_callback_list = NULL;
	}
	if (g_sender_info_list) {
		g_hash_table_destroy(g_sender_info_list);
		g_sender_info_list = NULL;
	}

	pthread_mutex_destroy(&callback_list_mutex);
}