ubus實現進程間通信

本文章轉載自:原博客鏈接

1. invoke的方式實現端對端通信

最簡單的情景就是一個提供服務的server端,一個請求服務的client端,client請求server的服務。
下面的例子中,server註冊了一個名爲“scan_prog”的對象,該對象中提供一個“scan”方法:
ubus_invoke.h:
  1. #ifndef __UBUS_INVOKE_H__  
  2. #define __UBUS_INVOKE_H__  
  3. #include <json/json.h>  
  4. #include <libubox/blobmsg_json.h>  
  5.   
  6.   
  7. struct prog_attr {  
  8. <span style="white-space:pre">  </span>char name[64];  
  9. <span style="white-space:pre">  </span>int chn_id;  
  10. };  
  11. #define PROG_MAX<span style="white-space:pre">  </span>8  
  12.   
  13.   
  14. #endif  /* __UBUS_INVOKE_H__ */  
#ifndef __UBUS_INVOKE_H__
#define __UBUS_INVOKE_H__
#include <json/json.h>
#include <libubox/blobmsg_json.h>


struct prog_attr {
	char name[64];
	int chn_id;
};
#define PROG_MAX	8


#endif  /* __UBUS_INVOKE_H__ */

invoke_server.c:

  1. #include <libubox/uloop.h>  
  2. #include <libubox/ustream.h>  
  3. #include <libubox/utils.h>  
  4. #include <libubus.h>  
  5. #include <json/json.h>  
  6. #include <libubox/blobmsg_json.h>  
  7. #include "ubus_invoke.h"  
  8.   
  9. static struct ubus_context * ctx = NULL;  
  10. static struct blob_buf b;  
  11. static const char * sock_path;  
  12.   
  13. static struct prog_attr uri_list[PROG_MAX] =   
  14. {  
  15.     {"program_beijing", 1},  
  16.     {"program_guangzhou", 2},  
  17.     {"program_shanghai", 3},  
  18.     {"", -1},  
  19. };  
  20.   
  21. enum  
  22. {  
  23.     SCAN_CHNID,  
  24.     SCAN_POLICY_MAX,  
  25. };  
  26.   
  27. static const struct blobmsg_policy scan_policy[SCAN_POLICY_MAX] = {  
  28.     [SCAN_CHNID] = {.name = "chnID", .type = BLOBMSG_TYPE_INT32},  
  29. };  
  30.   
  31. static int ubus_start_scan(struct ubus_context *ctx, struct ubus_object *obj,  
  32.               struct ubus_request_data *req, const char *method,  
  33.               struct blob_attr *msg)  
  34. {  
  35.     int ret = -1;  
  36.     void * json_list = NULL;  
  37.     void * json_uri = NULL;  
  38.     int idx;  
  39.   
  40.     blob_buf_init(&b, 0);  
  41.       
  42.     struct blob_attr *tb[SCAN_POLICY_MAX];  
  43.     blobmsg_parse(scan_policy, SCAN_POLICY_MAX, tb, blob_data(msg), blob_len(msg));  
  44.   
  45.     /* 
  46.     本例子中,如果請求特定的節目號,返回節目名稱。 
  47.     如果請求節目號是0,則返回所有節目列表。 
  48.     */  
  49.     if (tb[SCAN_CHNID] != NULL)  
  50.     {  
  51.         int chnid = blobmsg_get_u32(tb[SCAN_CHNID]);  
  52.   
  53.         if (chnid == 0)  
  54.         {  
  55.             json_uri = blobmsg_open_array(&b, "prog_list");  
  56.             for (idx = 0; idx < PROG_MAX; idx++)  
  57.             {  
  58.                 if ('\0' != uri_list[idx].name[0])  
  59.                 {  
  60.                     json_list = blobmsg_open_table(&b, NULL);  
  61.                     blobmsg_add_string(&b, "name", uri_list[idx].name);  
  62.                     blobmsg_add_u32(&b, "channel", uri_list[idx].chn_id);  
  63.                     blobmsg_close_table(&b, json_list);  
  64.                 }  
  65.             }  
  66.             blobmsg_close_array(&b, json_uri);  
  67.             ret = 0;  
  68.         }  
  69.         else  
  70.         {  
  71.             for (idx = 0; idx < PROG_MAX; idx++)  
  72.             {  
  73.                 if ('\0' != uri_list[idx].name[0] && uri_list[idx].chn_id == chnid)  
  74.                 {  
  75.                     blobmsg_add_string(&b, "name", uri_list[idx].name);  
  76.                     ret = 0;  
  77.                 }  
  78.             }  
  79.         }  
  80.     }  
  81.       
  82.     blobmsg_add_u32(&b, "result", ret);  
  83.     ubus_send_reply(ctx, req, b.head);  
  84.       
  85.     return 0;  
  86. }  
  87.   
  88. /* 方法列表 */  
  89. static struct ubus_method scan_methods[] =   
  90. {  
  91.     UBUS_METHOD("scan", ubus_start_scan, scan_policy),  
  92. };  
  93.   
  94. /* type目前沒有實際用處 */  
  95. static struct ubus_object_type scan_obj_type  
  96. = UBUS_OBJECT_TYPE("scan_prog", scan_methods);  
  97.   
  98. static struct ubus_object scan_obj =   
  99. {  
  100.     .name = "scan_prog"/* 對象的名字 */  
  101.     .type = &scan_obj_type,  
  102.     .methods = scan_methods,  
  103.     .n_methods = ARRAY_SIZE(scan_methods),  
  104. };  
  105.   
  106. static void ubus_add_fd(void)  
  107. {  
  108.     ubus_add_uloop(ctx);  
  109.   
  110. #ifdef FD_CLOEXEC  
  111.     fcntl(ctx->sock.fd, F_SETFD,  
  112.         fcntl(ctx->sock.fd, F_GETFD) | FD_CLOEXEC);  
  113. #endif  
  114. }  
  115.   
  116. static void ubus_reconn_timer(struct uloop_timeout *timeout)  
  117. {  
  118.     static struct uloop_timeout retry =  
  119.     {  
  120.         .cb = ubus_reconn_timer,  
  121.     };  
  122.     int t = 2;  
  123.   
  124.     if (ubus_reconnect(ctx, sock_path) != 0) {  
  125.         uloop_timeout_set(&retry, t * 1000);  
  126.         return;  
  127.     }  
  128.   
  129.     ubus_add_fd();  
  130. }  
  131.   
  132. static void ubus_connection_lost(struct ubus_context *ctx)  
  133. {  
  134.     ubus_reconn_timer(NULL);  
  135. }  
  136.   
  137. static int display_ubus_init(const char *path)  
  138. {  
  139.     uloop_init();  
  140.     sock_path = path;  
  141.   
  142.     ctx = ubus_connect(path);  
  143.     if (!ctx)  
  144.     {  
  145.         printf("ubus connect failed\n");  
  146.         return -1;  
  147.     }  
  148.       
  149.     printf("connected as %08x\n", ctx->local_id);  
  150.     ctx->connection_lost = ubus_connection_lost;  
  151.   
  152.     ubus_add_fd();  
  153.   
  154.     /* 向ubusd註冊對象和方法,供其他ubus客戶端調用。 */  
  155.     if (ubus_add_object(ctx, &scan_obj) != 0)  
  156.     {  
  157.         printf("ubus add obj failed\n");  
  158.         return -1;  
  159.     }  
  160.   
  161.     return 0;  
  162. }  
  163.   
  164. static void display_ubus_done(void)  
  165. {  
  166.     if (ctx)  
  167.         ubus_free(ctx);  
  168. }  
  169.   
  170. int main(int argc, char * argv[])  
  171. {  
  172.     char * path = NULL;  
  173.       
  174.     if (-1 == display_ubus_init(path))  
  175.     {  
  176.         printf("ubus connect failed!\n");  
  177.         return -1;  
  178.     }  
  179.   
  180.     uloop_run();  
  181.   
  182.     display_ubus_done();  
  183.   
  184.     return 0;  
  185. }  
#include <libubox/uloop.h>
#include <libubox/ustream.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <json/json.h>
#include <libubox/blobmsg_json.h>
#include "ubus_invoke.h"

static struct ubus_context * ctx = NULL;
static struct blob_buf b;
static const char * sock_path;

static struct prog_attr uri_list[PROG_MAX] = 
{
	{"program_beijing", 1},
	{"program_guangzhou", 2},
	{"program_shanghai", 3},
	{"", -1},
};

enum
{
	SCAN_CHNID,
	SCAN_POLICY_MAX,
};

static const struct blobmsg_policy scan_policy[SCAN_POLICY_MAX] = {
	[SCAN_CHNID] = {.name = "chnID", .type = BLOBMSG_TYPE_INT32},
};

static int ubus_start_scan(struct ubus_context *ctx, struct ubus_object *obj,
		      struct ubus_request_data *req, const char *method,
		      struct blob_attr *msg)
{
	int ret = -1;
	void * json_list = NULL;
	void * json_uri = NULL;
	int idx;

	blob_buf_init(&b, 0);
	
	struct blob_attr *tb[SCAN_POLICY_MAX];
	blobmsg_parse(scan_policy, SCAN_POLICY_MAX, tb, blob_data(msg), blob_len(msg));

	/*
	本例子中,如果請求特定的節目號,返回節目名稱。
	如果請求節目號是0,則返回所有節目列表。
	*/
	if (tb[SCAN_CHNID] != NULL)
	{
		int chnid = blobmsg_get_u32(tb[SCAN_CHNID]);

		if (chnid == 0)
		{
			json_uri = blobmsg_open_array(&b, "prog_list");
			for (idx = 0; idx < PROG_MAX; idx++)
			{
				if ('\0' != uri_list[idx].name[0])
				{
					json_list = blobmsg_open_table(&b, NULL);
					blobmsg_add_string(&b, "name", uri_list[idx].name);
					blobmsg_add_u32(&b, "channel", uri_list[idx].chn_id);
					blobmsg_close_table(&b, json_list);
				}
			}
			blobmsg_close_array(&b, json_uri);
			ret = 0;
		}
		else
		{
			for (idx = 0; idx < PROG_MAX; idx++)
			{
				if ('\0' != uri_list[idx].name[0] && uri_list[idx].chn_id == chnid)
				{
					blobmsg_add_string(&b, "name", uri_list[idx].name);
					ret = 0;
				}
			}
		}
	}
	
	blobmsg_add_u32(&b, "result", ret);
	ubus_send_reply(ctx, req, b.head);
	
	return 0;
}

/* 方法列表 */
static struct ubus_method scan_methods[] = 
{
	UBUS_METHOD("scan", ubus_start_scan, scan_policy),
};

/* type目前沒有實際用處 */
static struct ubus_object_type scan_obj_type
= UBUS_OBJECT_TYPE("scan_prog", scan_methods);

static struct ubus_object scan_obj = 
{
	.name = "scan_prog", /* 對象的名字 */
	.type = &scan_obj_type,
	.methods = scan_methods,
	.n_methods = ARRAY_SIZE(scan_methods),
};

static void ubus_add_fd(void)
{
	ubus_add_uloop(ctx);

#ifdef FD_CLOEXEC
	fcntl(ctx->sock.fd, F_SETFD,
		fcntl(ctx->sock.fd, F_GETFD) | FD_CLOEXEC);
#endif
}

static void ubus_reconn_timer(struct uloop_timeout *timeout)
{
	static struct uloop_timeout retry =
	{
		.cb = ubus_reconn_timer,
	};
	int t = 2;

	if (ubus_reconnect(ctx, sock_path) != 0) {
		uloop_timeout_set(&retry, t * 1000);
		return;
	}

	ubus_add_fd();
}

static void ubus_connection_lost(struct ubus_context *ctx)
{
	ubus_reconn_timer(NULL);
}

static int display_ubus_init(const char *path)
{
	uloop_init();
	sock_path = path;

	ctx = ubus_connect(path);
	if (!ctx)
	{
		printf("ubus connect failed\n");
		return -1;
	}
	
	printf("connected as %08x\n", ctx->local_id);
	ctx->connection_lost = ubus_connection_lost;

	ubus_add_fd();

	/* 向ubusd註冊對象和方法,供其他ubus客戶端調用。 */
	if (ubus_add_object(ctx, &scan_obj) != 0)
	{
		printf("ubus add obj failed\n");
		return -1;
	}

	return 0;
}

static void display_ubus_done(void)
{
	if (ctx)
		ubus_free(ctx);
}

int main(int argc, char * argv[])
{
	char * path = NULL;
	
	if (-1 == display_ubus_init(path))
	{
		printf("ubus connect failed!\n");
		return -1;
	}

	uloop_run();

	display_ubus_done();

	return 0;
}
客戶端代碼invoke_client.c去調用server端的"scan_prog"對象的"scan"方法:
  1. #include <libubox/uloop.h>  
  2. #include <libubox/ustream.h>  
  3. #include <libubox/utils.h>  
  4. #include <libubus.h>  
  5. #include <json/json.h>  
  6. #include <libubox/blobmsg_json.h>  
  7. #include "ubus_invoke.h"  
  8.   
  9. static struct ubus_context * ctx = NULL;  
  10. static struct blob_buf b;  
  11. static const char * cli_path;  
  12.   
  13. enum  
  14. {  
  15.     SCAN_CHNID,  
  16.     SCAN_POLICY_MAX,  
  17. };  
  18.   
  19. static const struct blobmsg_policy scan_policy[SCAN_POLICY_MAX] = {  
  20.     [SCAN_CHNID] = {.name = "chnID", .type = BLOBMSG_TYPE_INT32},  
  21. };  
  22.   
  23. static int timeout = 30;  
  24. static bool simple_output = false;  
  25.   
  26. static void scanreq_prog_cb(struct ubus_request *req, int type, struct blob_attr *msg)  
  27. {  
  28.     char *str;  
  29.     if (!msg)  
  30.         return;  
  31.   
  32.     /*  
  33.     在這裏處理返回的消息。 
  34.     本例子只是將返回的消息打印出來。 
  35.     */  
  36.     str = blobmsg_format_json_indent(msg, true, simple_output ? -1 : 0);  
  37.     printf("%s\n", str);  
  38.     free(str);  
  39. }  
  40.   
  41. static int client_ubus_call()  
  42. {  
  43.     unsigned int id;  
  44.     int ret;  
  45.   
  46.     blob_buf_init(&b, 0);  
  47.   
  48.     /* 需要傳遞的參數 */  
  49.     blobmsg_add_u32(&b, scan_policy[SCAN_CHNID].name, 0);  
  50.   
  51.     /* 
  52.     向ubusd查詢是否存在"scan_prog"這個對象, 
  53.     如果存在,返回其id 
  54.     */  
  55.     ret = ubus_lookup_id(ctx, "scan_prog", &id);  
  56.     if (ret != UBUS_STATUS_OK) {  
  57.         printf("lookup scan_prog failed\n");  
  58.         return ret;  
  59.     }  
  60.     else {  
  61.         printf("lookup scan_prog successs\n");  
  62.     }  
  63.       
  64.     /* 調用"scan_prog"對象的"scan"方法 */  
  65.     return ubus_invoke(ctx, id, "scan", b.head, scanreq_prog_cb, NULL, timeout * 1000);  
  66. }  
  67.   
  68. /* 
  69. ubus_invoke()的聲明如下: 
  70. int ubus_invoke(struct ubus_context *ctx, uint32_t obj, const char *method, 
  71.                 struct blob_attr *msg, ubus_data_handler_t cb, void *priv, int timeout); 
  72.                  
  73. 其中cb參數是消息回調函數,其函數類型定義爲: 
  74. typedef void (*ubus_data_handler_t)(struct ubus_request *req, 
  75.                     int type, struct blob_attr *msg); 
  76. 參數type是請求消息的類型,如UBUS_MSG_INVOKE,UBUS_MSG_DATA等。 
  77.  
  78. 參數msg存放從服務端得到的回覆消息,struct blob_attr類型,同樣用blobmsg_parse()來解析。 
  79.  
  80. 參數req保存了請求方的消息屬性,其中req->priv即ubus_invoke()中的priv參數, 
  81. 用這個參數可以零活的傳遞一些額外的數據。 
  82. */  
  83.   
  84. static int client_ubus_init(const char *path)  
  85. {  
  86.     uloop_init();  
  87.     cli_path = path;  
  88.   
  89.     ctx = ubus_connect(path);  
  90.     if (!ctx)  
  91.     {  
  92.         printf("ubus connect failed\n");  
  93.         return -1;  
  94.     }  
  95.       
  96.     printf("connected as %08x\n", ctx->local_id);  
  97.   
  98.     return 0;  
  99. }  
  100.   
  101. static void client_ubus_done(void)  
  102. {  
  103.     if (ctx)  
  104.         ubus_free(ctx);  
  105. }  
  106.   
  107. int main(int argc, char * argv[])  
  108. {  
  109.     /* ubusd創建的unix socket,默認值爲"/var/run/ubus.sock" */  
  110.     char * path = NULL;  
  111.   
  112.     /* 連接ubusd */  
  113.     if (UBUS_STATUS_OK != client_ubus_init(path))  
  114.     {  
  115.         printf("ubus connect failed!\n");  
  116.         return -1;  
  117.     }  
  118.   
  119.     /* 調用某個ubus方法並處理返回結果 */  
  120.     client_ubus_call();  
  121.   
  122.     client_ubus_done();  
  123.   
  124.     return 0;  
  125. }  
#include <libubox/uloop.h>
#include <libubox/ustream.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <json/json.h>
#include <libubox/blobmsg_json.h>
#include "ubus_invoke.h"

static struct ubus_context * ctx = NULL;
static struct blob_buf b;
static const char * cli_path;

enum
{
	SCAN_CHNID,
	SCAN_POLICY_MAX,
};

static const struct blobmsg_policy scan_policy[SCAN_POLICY_MAX] = {
	[SCAN_CHNID] = {.name = "chnID", .type = BLOBMSG_TYPE_INT32},
};

static int timeout = 30;
static bool simple_output = false;

static void scanreq_prog_cb(struct ubus_request *req, int type, struct blob_attr *msg)
{
	char *str;
	if (!msg)
		return;

	/* 
	在這裏處理返回的消息。
	本例子只是將返回的消息打印出來。
	*/
	str = blobmsg_format_json_indent(msg, true, simple_output ? -1 : 0);
	printf("%s\n", str);
	free(str);
}

static int client_ubus_call()
{
	unsigned int id;
	int ret;

	blob_buf_init(&b, 0);

	/* 需要傳遞的參數 */
	blobmsg_add_u32(&b, scan_policy[SCAN_CHNID].name, 0);

	/*
	向ubusd查詢是否存在"scan_prog"這個對象,
	如果存在,返回其id
	*/
	ret = ubus_lookup_id(ctx, "scan_prog", &id);
	if (ret != UBUS_STATUS_OK) {
		printf("lookup scan_prog failed\n");
		return ret;
	}
	else {
		printf("lookup scan_prog successs\n");
	}
	
	/* 調用"scan_prog"對象的"scan"方法 */
	return ubus_invoke(ctx, id, "scan", b.head, scanreq_prog_cb, NULL, timeout * 1000);
}

/*
ubus_invoke()的聲明如下:
int ubus_invoke(struct ubus_context *ctx, uint32_t obj, const char *method,
                struct blob_attr *msg, ubus_data_handler_t cb, void *priv, int timeout);
                
其中cb參數是消息回調函數,其函數類型定義爲:
typedef void (*ubus_data_handler_t)(struct ubus_request *req,
				    int type, struct blob_attr *msg);
參數type是請求消息的類型,如UBUS_MSG_INVOKE,UBUS_MSG_DATA等。

參數msg存放從服務端得到的回覆消息,struct blob_attr類型,同樣用blobmsg_parse()來解析。

參數req保存了請求方的消息屬性,其中req->priv即ubus_invoke()中的priv參數,
用這個參數可以零活的傳遞一些額外的數據。
*/

static int client_ubus_init(const char *path)
{
	uloop_init();
	cli_path = path;

	ctx = ubus_connect(path);
	if (!ctx)
	{
		printf("ubus connect failed\n");
		return -1;
	}
	
	printf("connected as %08x\n", ctx->local_id);

	return 0;
}

static void client_ubus_done(void)
{
	if (ctx)
		ubus_free(ctx);
}

int main(int argc, char * argv[])
{
	/* ubusd創建的unix socket,默認值爲"/var/run/ubus.sock" */
	char * path = NULL;

	/* 連接ubusd */
	if (UBUS_STATUS_OK != client_ubus_init(path))
	{
		printf("ubus connect failed!\n");
		return -1;
	}

	/* 調用某個ubus方法並處理返回結果 */
	client_ubus_call();

	client_ubus_done();

	return 0;
}
先執行server程序,再執行client程序,可以看到client發出請求後,server返回了相應的節目號,在client打印出了接收到的消息。
也可以通過shell命令來請求server的服務,例如:
ubus call scan_prog scan '{"chnID": 0}'
這條命令和執行client程序的作用相同。

2. subscribe/notify的方式實現訂閱

ubus支持以訂閱的方式進行進程間通信,通信方式如下圖:


被訂閱者(server)又稱爲notifier,訂閱者(client)又稱爲subscriber。這樣一來,可以同時有多個client訂閱server的某個服務,當server通過該服務廣播消息時,所有client都會被通知,並執行各自的處理。

主要過程爲:

server進程:

  1. 連接ubusd。
  2. 註冊一個object,用於client訂閱。
  3. server可以向訂閱者廣播消息。
  4. 等待消息。

client進程:

  1. 連接ubusd。
  2. 向server訂閱object,並定義收到server的消息時的處理函數。
  3. 等待消息。

代碼示例:下面這個例子中,server註冊了一個名爲“test”的對象,並定期廣播消息。而client去訂閱這個對象,並對server發出的消息做處理。

notify_server.c:

  1. #include <unistd.h>  
  2. #include <libubox/blobmsg_json.h>  
  3. #include <libubox/uloop.h>  
  4. #include <libubus.h>  
  5.   
  6. static struct ubus_context *ctx;  
  7.   
  8. static void test_client_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj)  
  9. {  
  10.     fprintf(stderr, "Subscribers active: %d\n", obj->has_subscribers);  
  11. }  
  12.   
  13. /* 這個空的method列表,只是爲了讓object有個名字,這樣client可以通過object name來訂閱。 */  
  14. static struct ubus_method test_methods[] = {};  
  15.   
  16. static struct ubus_object_type test_obj_type =   
  17.     UBUS_OBJECT_TYPE("test", test_methods);  
  18.   
  19. static struct ubus_object test_object = {  
  20.     .name = "test"/* object的名字 */  
  21.     .type = &test_obj_type,  
  22.     .subscribe_cb = test_client_subscribe_cb,  
  23. };  
  24.   
  25. static void notifier_main(void)  
  26. {  
  27.     int ret;  
  28.   
  29.     /* 註冊一個object,client可以訂閱這個object */  
  30.     ret = ubus_add_object(ctx, &test_object);  
  31.     if (ret) {  
  32.         fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret));  
  33.         return;  
  34.     }  
  35.   
  36.     /* 在需要的時候,向所有客戶端發送notify消息 */  
  37.       
  38.     /* step1: 如果需要傳遞參數,則保存到struct blob_attr類型的結構體中。 */  
  39.   
  40.     /*  
  41.     int ubus_notify(struct ubus_context *ctx, struct ubus_object *obj, const char *type, struct blob_attr *msg, int timeout); 
  42.     type是一個字符串,自定義的。msg是需要攜帶的參數。如果需要等待回覆,timeout需設置爲>=0。 
  43.     */  
  44.     while (1) {  
  45.         sleep(2);  
  46.         /* step2: 廣播notification消息。 */  
  47.         ubus_notify(ctx,  &test_object, "say Hi!", NULL, -1);  
  48.     }  
  49. }  
  50.   
  51. int main(int argc, char **argv)  
  52. {  
  53.     const char *ubus_socket = NULL;  
  54.   
  55.     uloop_init();  
  56.   
  57.     ctx = ubus_connect(ubus_socket);  
  58.     if (!ctx) {  
  59.         fprintf(stderr, "Failed to connect to ubus\n");  
  60.         return -1;  
  61.     }  
  62.   
  63.     ubus_add_uloop(ctx);  
  64.   
  65.     notifier_main();  
  66.       
  67.     uloop_run();  
  68.   
  69.     ubus_free(ctx);  
  70.     uloop_done();  
  71.   
  72.     return 0;  
  73. }  
#include <unistd.h>
#include <libubox/blobmsg_json.h>
#include <libubox/uloop.h>
#include <libubus.h>

static struct ubus_context *ctx;

static void test_client_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj)
{
	fprintf(stderr, "Subscribers active: %d\n", obj->has_subscribers);
}

/* 這個空的method列表,只是爲了讓object有個名字,這樣client可以通過object name來訂閱。 */
static struct ubus_method test_methods[] = {};

static struct ubus_object_type test_obj_type = 
	UBUS_OBJECT_TYPE("test", test_methods);

static struct ubus_object test_object = {
	.name = "test", /* object的名字 */
	.type = &test_obj_type,
	.subscribe_cb = test_client_subscribe_cb,
};

static void notifier_main(void)
{
	int ret;

	/* 註冊一個object,client可以訂閱這個object */
	ret = ubus_add_object(ctx, &test_object);
	if (ret) {
		fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret));
		return;
	}

	/* 在需要的時候,向所有客戶端發送notify消息 */
	
	/* step1: 如果需要傳遞參數,則保存到struct blob_attr類型的結構體中。 */

	/* 
	int ubus_notify(struct ubus_context *ctx, struct ubus_object *obj, const char *type, struct blob_attr *msg, int timeout);
	type是一個字符串,自定義的。msg是需要攜帶的參數。如果需要等待回覆,timeout需設置爲>=0。
	*/
	while (1) {
		sleep(2);
		/* step2: 廣播notification消息。 */
		ubus_notify(ctx,  &test_object, "say Hi!", NULL, -1);
	}
}

int main(int argc, char **argv)
{
	const char *ubus_socket = NULL;

	uloop_init();

	ctx = ubus_connect(ubus_socket);
	if (!ctx) {
		fprintf(stderr, "Failed to connect to ubus\n");
		return -1;
	}

	ubus_add_uloop(ctx);

	notifier_main();
	
	uloop_run();

	ubus_free(ctx);
	uloop_done();

	return 0;
}
notify_client.c:客戶端訂閱“test”對象,在收到3次消息後,隨即取消對“test”對象的訂閱。

  1. #include <unistd.h>  
  2. #include <libubox/blobmsg_json.h>  
  3. #include <libubox/uloop.h>  
  4. #include <libubus.h>  
  5.   
  6. static struct ubus_context *ctx;  
  7.   
  8. static int counter = 0;  
  9. static uint32_t obj_id;  
  10. static struct ubus_subscriber test_event;  
  11.   
  12. static int test_notify(struct ubus_context *ctx, struct ubus_object *obj,  
  13.                   struct ubus_request_data *req,  
  14.                   const char *method, struct blob_attr *msg)  
  15. {  
  16.     printf("notify handler...\n");  
  17.     counter++;  
  18.     if (counter > 3)  
  19.         ubus_unsubscribe(ctx, &test_event, obj_id); /* 取消訂閱 */  
  20.     return 0;  
  21. }  
  22.   
  23. static void test_handle_remove(struct ubus_context *ctx,  
  24.                       struct ubus_subscriber *obj, uint32_t id)  
  25. {  
  26.     printf("remove handler...\n");  
  27. }  
  28.   
  29. static void subscriber_main(void)  
  30. {  
  31.     int ret;  
  32.       
  33.     /* 通知到來時的處理函數。 */  
  34.     test_event.cb = test_notify;  
  35.     test_event.remove_cb = test_handle_remove; //server主動發起刪除該client的訂閱的cb函數(如server退出的時候)  
  36.   
  37.     /* 註冊test_event */  
  38.     ret = ubus_register_subscriber(ctx, &test_event);  
  39.     if (ret)  
  40.         fprintf(stderr, "Failed to add watch handler: %s\n", ubus_strerror(ret));  
  41.       
  42.     /* 得到要訂閱的object的id */  
  43.     ret = ubus_lookup_id(ctx, "test", &obj_id);  
  44.     if (ret)  
  45.         fprintf(stderr, "Failed to lookup object: %s\n", ubus_strerror(ret));  
  46.   
  47.     /* 訂閱object */  
  48.     ret = ubus_subscribe(ctx, &test_event, obj_id);  
  49.     if (ret)  
  50.         fprintf(stderr, "Failed to subscribe: %s\n", ubus_strerror(ret));  
  51. }  
  52.   
  53. int main(int argc, char **argv)  
  54. {  
  55.     const char *ubus_socket = NULL;  
  56.   
  57.     uloop_init();  
  58.   
  59.     ctx = ubus_connect(ubus_socket);  
  60.     if (!ctx) {  
  61.         fprintf(stderr, "Failed to connect to ubus\n");  
  62.         return -1;  
  63.     }  
  64.   
  65.     ubus_add_uloop(ctx);  
  66.   
  67.     subscriber_main();  
  68.       
  69.     uloop_run();  
  70.   
  71.     ubus_free(ctx);  
  72.     uloop_done();  
  73.   
  74.     return 0;  
  75. }  
#include <unistd.h>
#include <libubox/blobmsg_json.h>
#include <libubox/uloop.h>
#include <libubus.h>

static struct ubus_context *ctx;

static int counter = 0;
static uint32_t obj_id;
static struct ubus_subscriber test_event;

static int test_notify(struct ubus_context *ctx, struct ubus_object *obj,
			      struct ubus_request_data *req,
			      const char *method, struct blob_attr *msg)
{
	printf("notify handler...\n");
	counter++;
	if (counter > 3)
		ubus_unsubscribe(ctx, &test_event, obj_id); /* 取消訂閱 */
	return 0;
}

static void test_handle_remove(struct ubus_context *ctx,
				      struct ubus_subscriber *obj, uint32_t id)
{
	printf("remove handler...\n");
}

static void subscriber_main(void)
{
	int ret;
	
	/* 通知到來時的處理函數。 */
	test_event.cb = test_notify;
	test_event.remove_cb = test_handle_remove; //server主動發起刪除該client的訂閱的cb函數(如server退出的時候)

	/* 註冊test_event */
	ret = ubus_register_subscriber(ctx, &test_event);
	if (ret)
		fprintf(stderr, "Failed to add watch handler: %s\n", ubus_strerror(ret));
    
	/* 得到要訂閱的object的id */
	ret = ubus_lookup_id(ctx, "test", &obj_id);
	if (ret)
		fprintf(stderr, "Failed to lookup object: %s\n", ubus_strerror(ret));

	/* 訂閱object */
	ret = ubus_subscribe(ctx, &test_event, obj_id);
	if (ret)
		fprintf(stderr, "Failed to subscribe: %s\n", ubus_strerror(ret));
}

int main(int argc, char **argv)
{
	const char *ubus_socket = NULL;

	uloop_init();

	ctx = ubus_connect(ubus_socket);
	if (!ctx) {
		fprintf(stderr, "Failed to connect to ubus\n");
		return -1;
	}

	ubus_add_uloop(ctx);

	subscriber_main();
	
	uloop_run();

	ubus_free(ctx);
	uloop_done();

	return 0;
}
先運行server&,註冊可訂閱的對象“test”,並隨即每2秒向外廣播通知消息。這時還沒有client訂閱這個對象。
運行多個client程序,由於每個client都訂閱了“test”對象,則所有client都會收到server發出的消息。當client取消訂閱後,則不再收到server端的消息。

event的方式實現事件通知

event機制從一對一的進程之間通信來講,和invoke機制類似。不過event機制中,發送方不需要知道誰要接收這個消息,實際上就是一個廣播消息。這類似於U盤的熱插拔:當插上或拔出U盤時,內核會廣播一個NETLINK事件,之後內核繼續做自己的事情,而不關心誰會去監聽和處理這個事件。

下面的例子中,client端同時監聽了“add_device”和“rm_device”兩個事件,而server端會觸發“add_device”事件並攜帶device的一些信息發送出去。

event_client.c:

  1. #include <libubox/uloop.h>  
  2. #include <libubox/ustream.h>  
  3. #include <libubox/utils.h>  
  4. #include <libubus.h>  
  5. #include <json/json.h>  
  6. #include <libubox/blobmsg_json.h>  
  7.   
  8. static struct ubus_context * ctx = NULL;  
  9. static const char * cli_path;  
  10.   
  11. #define UBUS_EVENT_ADD_DEVICE   "add_device"  
  12. #define UBUS_EVENT_REMOVE_DEVICE    "rm_device"  
  13.   
  14. static void ubus_probe_device_event(struct ubus_context *ctx, struct ubus_event_handler *ev,  
  15.               const char *type, struct blob_attr *msg)  
  16. {  
  17.     char *str;  
  18.   
  19.     if (!msg)  
  20.         return;  
  21.   
  22.     /*  
  23.     在這裏實現收到事件後的動作。 
  24.     event也可以傳遞消息,放在msg中。 
  25.      
  26.     本例子只是將返回的消息打印出來。 
  27.     */  
  28.     str = blobmsg_format_json(msg, true);  
  29.     printf("{ \"%s\": %s }\n", type, str);  
  30.     free(str);  
  31. }  
  32.   
  33. static int client_ubus_register_events()  
  34. {  
  35.     static struct ubus_event_handler listener;  
  36.     int ret = 0;  
  37.   
  38.     /* 註冊特定event的listener。多個event可以使用同一個listener */  
  39.     memset(&listener, 0, sizeof(listener));  
  40.     listener.cb = ubus_probe_device_event;  
  41.       
  42.     ret = ubus_register_event_handler(ctx, &listener, UBUS_EVENT_ADD_DEVICE);  
  43.     if (ret)  
  44.     {  
  45.         fprintf(stderr, "register event failed.\n");  
  46.         return -1;  
  47.     }  
  48.   
  49.     ret = ubus_register_event_handler(ctx, &listener, UBUS_EVENT_REMOVE_DEVICE);  
  50.     if (ret)  
  51.     {  
  52.         ubus_unregister_event_handler(ctx, &listener);  
  53.         fprintf(stderr, "register event failed.\n");  
  54.         return -1;  
  55.     }  
  56.   
  57.     return 0;  
  58. }  
  59.   
  60. static void ubus_add_fd(void)  
  61. {  
  62.     ubus_add_uloop(ctx);  
  63.   
  64. #ifdef FD_CLOEXEC  
  65.     fcntl(ctx->sock.fd, F_SETFD,  
  66.         fcntl(ctx->sock.fd, F_GETFD) | FD_CLOEXEC);  
  67. #endif  
  68. }  
  69.   
  70. static void ubus_reconn_timer(struct uloop_timeout *timeout)  
  71. {  
  72.     static struct uloop_timeout retry =  
  73.     {  
  74.         .cb = ubus_reconn_timer,  
  75.     };  
  76.     int t = 2;  
  77.   
  78.     if (ubus_reconnect(ctx, cli_path) != 0) {  
  79.         uloop_timeout_set(&retry, t * 1000);  
  80.         return;  
  81.     }  
  82.   
  83.     ubus_add_fd();  
  84. }  
  85.   
  86. static void ubus_connection_lost(struct ubus_context *ctx)  
  87. {  
  88.     ubus_reconn_timer(NULL);  
  89. }  
  90.   
  91. static int client_ubus_init(const char *path)  
  92. {  
  93.     uloop_init();  
  94.     cli_path = path;  
  95.       
  96.     ctx = ubus_connect(path);  
  97.     if (!ctx)  
  98.     {  
  99.         printf("ubus connect failed\n");  
  100.         return -1;  
  101.     }  
  102.       
  103.     printf("connected as %08x\n", ctx->local_id);  
  104.     ctx->connection_lost = ubus_connection_lost;  
  105.   
  106.     ubus_add_fd();  
  107.   
  108.     return 0;  
  109. }  
  110.   
  111. static void client_ubus_done(void)  
  112. {  
  113.     if (ctx)  
  114.         ubus_free(ctx);  
  115. }  
  116.   
  117. int main(int argc, char * argv[])  
  118. {  
  119.     char * path = NULL;  
  120.   
  121.     /* 連接ubusd */  
  122.     if (UBUS_STATUS_OK != client_ubus_init(path))  
  123.     {  
  124.         printf("ubus connect failed!\n");  
  125.         return -1;  
  126.     }  
  127.   
  128.     /* 註冊某個事件的處理函數 */  
  129.     client_ubus_register_events();  
  130.   
  131.     uloop_run();  
  132.       
  133.     client_ubus_done();  
  134.   
  135.     return 0;  
  136. }  
#include <libubox/uloop.h>
#include <libubox/ustream.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <json/json.h>
#include <libubox/blobmsg_json.h>

static struct ubus_context * ctx = NULL;
static const char * cli_path;

#define	UBUS_EVENT_ADD_DEVICE	"add_device"
#define	UBUS_EVENT_REMOVE_DEVICE	"rm_device"

static void ubus_probe_device_event(struct ubus_context *ctx, struct ubus_event_handler *ev,
			  const char *type, struct blob_attr *msg)
{
	char *str;

	if (!msg)
		return;

	/* 
	在這裏實現收到事件後的動作。
	event也可以傳遞消息,放在msg中。
	
	本例子只是將返回的消息打印出來。
	*/
	str = blobmsg_format_json(msg, true);
	printf("{ \"%s\": %s }\n", type, str);
	free(str);
}

static int client_ubus_register_events()
{
	static struct ubus_event_handler listener;
	int ret = 0;

	/* 註冊特定event的listener。多個event可以使用同一個listener */
	memset(&listener, 0, sizeof(listener));
	listener.cb = ubus_probe_device_event;
	
	ret = ubus_register_event_handler(ctx, &listener, UBUS_EVENT_ADD_DEVICE);
	if (ret)
	{
		fprintf(stderr, "register event failed.\n");
		return -1;
	}

	ret = ubus_register_event_handler(ctx, &listener, UBUS_EVENT_REMOVE_DEVICE);
	if (ret)
	{
		ubus_unregister_event_handler(ctx, &listener);
		fprintf(stderr, "register event failed.\n");
		return -1;
	}

	return 0;
}

static void ubus_add_fd(void)
{
	ubus_add_uloop(ctx);

#ifdef FD_CLOEXEC
	fcntl(ctx->sock.fd, F_SETFD,
		fcntl(ctx->sock.fd, F_GETFD) | FD_CLOEXEC);
#endif
}

static void ubus_reconn_timer(struct uloop_timeout *timeout)
{
	static struct uloop_timeout retry =
	{
		.cb = ubus_reconn_timer,
	};
	int t = 2;

	if (ubus_reconnect(ctx, cli_path) != 0) {
		uloop_timeout_set(&retry, t * 1000);
		return;
	}

	ubus_add_fd();
}

static void ubus_connection_lost(struct ubus_context *ctx)
{
	ubus_reconn_timer(NULL);
}

static int client_ubus_init(const char *path)
{
	uloop_init();
	cli_path = path;
	
	ctx = ubus_connect(path);
	if (!ctx)
	{
		printf("ubus connect failed\n");
		return -1;
	}
	
	printf("connected as %08x\n", ctx->local_id);
	ctx->connection_lost = ubus_connection_lost;

	ubus_add_fd();

	return 0;
}

static void client_ubus_done(void)
{
	if (ctx)
		ubus_free(ctx);
}

int main(int argc, char * argv[])
{
	char * path = NULL;

	/* 連接ubusd */
	if (UBUS_STATUS_OK != client_ubus_init(path))
	{
		printf("ubus connect failed!\n");
		return -1;
	}

	/* 註冊某個事件的處理函數 */
	client_ubus_register_events();

	uloop_run();
	
	client_ubus_done();

	return 0;
}
event_server.c:

  1. #include <libubox/uloop.h>  
  2. #include <libubox/ustream.h>  
  3. #include <libubox/utils.h>  
  4. #include <libubus.h>  
  5. #include <json/json.h>  
  6. #include <libubox/blobmsg_json.h>  
  7.   
  8. static struct ubus_context * ctx = NULL;  
  9. static struct blob_buf b;  
  10. static const char * sock_path;  
  11.   
  12. static int server_ubus_send_event(void)  
  13. {  
  14.     blob_buf_init(&b, 0);  
  15.   
  16.     /* 需要傳遞的參數 */  
  17.     blobmsg_add_u32(&b, "major", 3);  
  18.     blobmsg_add_u32(&b, "minor", 56);  
  19.     blobmsg_add_string(&b, "name""mmc01");  
  20.   
  21.     /* 廣播名爲"add_device"的事件 */  
  22.     return ubus_send_event(ctx, "add_device", b.head);  
  23. }  
  24.   
  25. static int display_ubus_init(const char *path)  
  26. {  
  27.     uloop_init();  
  28.     sock_path = path;  
  29.   
  30.     ctx = ubus_connect(path);  
  31.     if (!ctx)  
  32.     {  
  33.         printf("ubus connect failed\n");  
  34.         return -1;  
  35.     }  
  36.       
  37.     printf("connected as %08x\n", ctx->local_id);  
  38.   
  39.     return 0;  
  40. }  
  41.   
  42. static void display_ubus_done(void)  
  43. {  
  44.     if (ctx)  
  45.         ubus_free(ctx);  
  46. }  
  47.   
  48. int main(int argc, char * argv[])  
  49. {  
  50.     char * path = NULL;  
  51.       
  52.     if (-1 == display_ubus_init(path))  
  53.     {  
  54.         printf("ubus connect failed!\n");  
  55.         return -1;  
  56.     }  
  57.   
  58.     server_ubus_send_event();  
  59.   
  60.     display_ubus_done();  
  61.   
  62.     return 0;  
  63. }  
#include <libubox/uloop.h>
#include <libubox/ustream.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <json/json.h>
#include <libubox/blobmsg_json.h>

static struct ubus_context * ctx = NULL;
static struct blob_buf b;
static const char * sock_path;

static int server_ubus_send_event(void)
{
	blob_buf_init(&b, 0);

	/* 需要傳遞的參數 */
	blobmsg_add_u32(&b, "major", 3);
	blobmsg_add_u32(&b, "minor", 56);
	blobmsg_add_string(&b, "name", "mmc01");

	/* 廣播名爲"add_device"的事件 */
	return ubus_send_event(ctx, "add_device", b.head);
}

static int display_ubus_init(const char *path)
{
	uloop_init();
	sock_path = path;

	ctx = ubus_connect(path);
	if (!ctx)
	{
		printf("ubus connect failed\n");
		return -1;
	}
	
	printf("connected as %08x\n", ctx->local_id);

	return 0;
}

static void display_ubus_done(void)
{
	if (ctx)
		ubus_free(ctx);
}

int main(int argc, char * argv[])
{
	char * path = NULL;
	
	if (-1 == display_ubus_init(path))
	{
		printf("ubus connect failed!\n");
		return -1;
	}

	server_ubus_send_event();

	display_ubus_done();

	return 0;
}
先運行client &註冊事件。我們同時啓動兩個client程序。
再執行server主動觸發"add_device"事件,則凡是註冊了這個事件的client都會收到該事件並執行各自的處理。
  1. root@NVR:~# ./server  
  2. connected as fdecbdc1  
  3. { "add_device": { "major": 3, "minor": 56, "name": "mmc01" } }  
  4. { "add_device": { "major": 3, "minor": 56, "name": "mmc01" } }  
root@NVR:~# ./server
connected as fdecbdc1
{ "add_device": { "major": 3, "minor": 56, "name": "mmc01" } }
{ "add_device": { "major": 3, "minor": 56, "name": "mmc01" } }
也可以通過命令行的ubus send命令觸發事件:
  1. root@NVR:~# ubus send "rm_device" '{ "major": 3, "minor": 56, "name": "mmc01" }'  
  2. { "rm_device": { "major": 3, "minor": 56, "name": "mmc01" } }  
  3. { "rm_device": { "major": 3, "minor": 56, "name": "mmc01" } }  
root@NVR:~# ubus send "rm_device" '{ "major": 3, "minor": 56, "name": "mmc01" }'
{ "rm_device": { "major": 3, "minor": 56, "name": "mmc01" } }
{ "rm_device": { "major": 3, "minor": 56, "name": "mmc01" } }

在使用ubus時,可根據實際的場景來選擇用哪種方式進行進程間通信。如之前所說,ubus是爲發送消息而設計的,不合適傳輸大量數據。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章