ubus客户端向服务器请求信息的例子

本文介绍OpenWRT环境下Ubus通信机制,通过一个客户端请求AP信息的实例,展示了服务器端如何处理请求并返回数据,同时给出了客户端代码实现。

openwrt用户空间进中通信常用的通信方式是使用ubus通信,下面给出一个例子,是客户端向服务器请求ap的个数。

 

服务器端代码

 

#include <unistd.h>
#include <signal.h>

#include "libubus.h"

static struct ubus_context *ctx;
static struct blob_buf b;

enum {
	REQ_STATION_COUNT,
	REQ_AP_NAME,
	__REQ_MAX
};

/* 接收的解析格式, client发送过来的调用是 blobmsg_add_u32(&b, "getcnt", ap_index); */
static const struct blobmsg_policy fun2_message_parse_policy[__REQ_MAX] = {
	[REQ_STATION_COUNT] = { .name = "getcnt", .type = BLOBMSG_TYPE_INT32 },
	[REQ_STATION_COUNT] = { .name = "apname", .type = BLOBMSG_TYPE_STRING },
};

static int fun2_handler(struct ubus_context *ctx, struct ubus_object *obj,
		      struct ubus_request_data *req, const char *method,
		      struct blob_attr *msg)
{
	struct blob_attr *tb[__REQ_MAX];
	//模拟ap数组中station的个数
	int ap_array[] = {-1, 11111,22222,33333};
	int ap_index;

	blobmsg_parse(fun2_message_parse_policy, __REQ_MAX, tb, blob_data(msg), blob_len(msg));
	if (!tb[REQ_STATION_COUNT])
		return UBUS_STATUS_INVALID_ARGUMENT;
		
	ap_index = blobmsg_get_u32(tb[REQ_STATION_COUNT]);
	
	fprintf(stdout, "someone ask the ap[%d]' info \n", ap_index);
	
	if (ap_index > (sizeof(ap_array) / sizeof(int)))
		ap_index = 0;
		
	
	blob_buf_init(&b, 0);
	//返回客户端请求的station个数
	blobmsg_add_u32(&b, "stacnt", ap_array[ap_index]);
	//发送
	ubus_send_reply(ctx, req, b.head);
	
	return 0;
}


static const struct ubus_method test_methods[] = {
	UBUS_METHOD("fun2", fun2_handler, fun2_message_parse_policy),
};

static struct ubus_object_type test_object_type =
	UBUS_OBJECT_TYPE("server_fun", test_methods);

	//定义一个ubus 对象。
	//其他进程通过调用相应字符串给执行对应的回调函数
	//注册成功后可以通过命令ubus list -v 查看到obj
	//' server_fun' @79a8beac
	// 		"fun2" : {"getcnt":"Iterger","apname":"String"}
static struct ubus_object test_object = {
	.name = "server_fun",
	.type = &test_object_type,
	.methods = test_methods,
	.n_methods = ARRAY_SIZE(test_methods),
};

static void server_main(void)
{
	int ret;

	//向ubusd新增一个对象,之后test_object在ubusd中有一个id号,其他进程通过该id号来向test_object发送消息。
	ret = ubus_add_object(ctx, &test_object);
	if (ret)
		fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret));

	uloop_run();
}

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

	while ((ch = getopt(argc, argv, "cs:")) != -1) {
		switch (ch) {
		case 's':
			ubus_socket = optarg;
			break;
		default:
			break;
		}
	}

	argc -= optind;
	argv += optind;

	uloop_init();
	signal(SIGPIPE, SIG_IGN);

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

	ubus_add_uloop(ctx);

	server_main();

	ubus_free(ctx);
	uloop_done();

	return 0;
}

 


客户端代码

 

#include <sys/time.h>
#include <unistd.h>

#include "libubus.h"

static struct ubus_context *ctx;
static struct blob_buf b;

enum {
	RETURN_CODE,
	__RETURN_MAX,
};

static const struct blobmsg_policy return_policy[__RETURN_MAX] = {
	[RETURN_CODE] = { .name = "stacnt", .type = BLOBMSG_TYPE_INT32 },
};

static void get_result_data_cb(struct ubus_request *req,
				    int type, struct blob_attr *msg)
{
	struct blob_attr *tb[__RETURN_MAX];
	int rc;
	
	//返回的数据也是有tlv格式的,按照return_policy格式进行解析。
	blobmsg_parse(return_policy, __RETURN_MAX, tb, blob_data(msg), blob_len(msg));
	if (!tb[RETURN_CODE]) {
		fprintf(stderr, "No return code received from server\n");
		return;
	}

	rc = blobmsg_get_u32(tb[RETURN_CODE]);

	fprintf(stderr, "get station number is %d \n", rc);
}

static void client_main(void)
{
	static struct ubus_request req;
	uint32_t id;
	int ret;
	int ap_index=2;

	//查找ubusd上的server_fun对象。该对象由server端注册
	if (ubus_lookup_id(ctx, "server_fun", &id)) {
		fprintf(stderr, "Failed to look up test object\n");
		return;
	}

	//b 为全局变量,初始化
	blob_buf_init(&b, 0);
	
	//添加发送buffer的tlv,格式由server_main.c的fun2_message_parse_policy定义
	blobmsg_add_u32(&b, "getcnt", ap_index);
	
	//触发server端注册的函数,并接收返回值,处理函数为get_result_data_cb
	ubus_invoke(ctx, id, "fun2", b.head, get_result_data_cb, NULL, 3000);
}

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

	while ((ch = getopt(argc, argv, "cs:")) != -1) {
		switch (ch) {
		case 's':
			ubus_socket = optarg;
			break;
		default:
			break;
		}
	}

	argc -= optind;
	argv += optind;

	uloop_init();

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

	ubus_add_uloop(ctx);

	client_main();

	ubus_free(ctx);
	uloop_done();

	return 0;
}


以上代码:服务器端向ubusd注册了个server_fun的服务器对象,客户端通过server_fun ubus对象与服务器端通信。

 

其他:ubus shell调用格式

ubus send linkinspect '{"ac_cmd":1}'

<think> 我们正在讨论的是引用[1]中的test_watch函数,这个函数是ubus示例代码中的一个回调函数。根据引用[1]的代码,我们可以推断: 1. 这个函数是ubus框架在收到特定请求时调用的。 2. 参数由ubus框架传入,具体含义如下: - `ctx`: ubus上下文,代表ubus连接,用于后续ubus操作(如发送回复、订阅事件等)。 - `obj`: 表示当前请求所针对的ubus对象(即这个回调函数所注册的对象)。 - `req`: 代表当前请求的详细信息,用于后续可能需要的回复操作。 - `method`: 字符串,表示调用的方法名(在这个例子中,方法名应该是"watch")。 - `msg`: 指向一个`blob_attr`结构体的指针,包含了客户端发送的消息数据(以属性块的形式)。 具体到`test_watch`函数,它用于订阅一个ubus对象的事件。函数内部解析`msg`参数,从中获取要订阅的对象的ID(WATCH_ID),然后调用`ubus_subscribe`进行订阅。 因此,这个函数的调用者是ubus框架(具体是ubusd守护进程或libubus在收到客户端请求时触发)。参数是由ubus框架在分发请求时传入的。 下面我们详细解释每个参数: - `struct ubus_context *ctx`: 这是ubus的上下文指针,它代表了与ubus通信的上下文环境。通过这个上下文,我们可以调用ubus提供的各种函数,比如`ubus_subscribe`。这个上下文通常由初始化时创建并维护。[^1] - `struct ubus_object *obj`: 指向当前ubus对象的指针。在ubus中,每个服务可以注册多个对象,每个对象有自己的方法和信号。当客户端调用某个对象的方法时,这个参数就指向该对象。[^1] - `struct ubus_request_data *req`: 这个结构体存储了当前请求信息。它通常用于在需要异步回复请求时保存请求状态,以便之后回复。注意,这个函数是同步的,所以在这个例子中并没有使用到它(但在其他方法中可能会使用)。[^1] - `const char *method`: 字符串,表示客户端请求调用的方法名。在这个例子中,因为函数名为`test_watch`,并且注册为处理"watch"方法,所以`method`的值应该是"watch"。[^1] - `struct blob_attr *msg`: 指向一个属性块数据的指针,这个属性块包含了客户端发送的参数。需要使用`blobmsg_parse`等函数来解析。在`test_watch`中,解析出`WATCH_ID`参数用于订阅。[^1] 参数传入方式:ubus框架在收到客户端请求后,根据请求中指定的对象和方法,找到对应的回调函数(此处为`test_watch`),然后将上述参数传递给它。 因此,针对用户的问题,总结如下: 调用者:ubus框架(具体为ubusd或libubus的消息分发机制)。 参数传入方式:由ubus框架在调用回调函数时传入,参数值来源于客户端请求。 另外,引用[2]提到的是HAL层的函数接口表,与ubus无关。引用[3]是关于DRM的原子提交,引用[4]是beego框架中获取请求参数的方法,这些与当前问题关联不大。 下面按照要求,生成相关问题。 </think> 基于提供的代码片段(引用[1]),`test_watch` 函数(即您提到的 `example_function`)的调用机制和参数含义如下: ### 1. 函数的调用者 - **调用者**:`test_watch` 函数由 **ubus IPC 框架** 在以下场景触发调用: - 当客户端通过 `ubus call` 命令请求服务器端注册的对象方法时 - 请求的方法名需与服务器注册时声明的方法名匹配(在此例中应为 `watch`) - 例如客户端命令:`ubus call object_name watch '{ "id": 123 }'`[^1] ### 2. 参数传入方式 - **框架自动注入**:所有参数均由 ubus 框架在收到客户端请求时自动解析并填充: 1. 客户端发送请求ubus 守护进程(`ubusd`) 2. `ubusd` 根据请求中的对象名和方法名路由到注册的 `test_watch` 函数 3. 框架解析请求数据并填充参数列表[^1][^4] ### 3. 参数详细含义 | 参数 | 类型 | 说明 | |-----------|---------------------------|----------------------------------------------------------------------| | `ctx` | `struct ubus_context*` | Ubus 通信上下文,包含连接状态、事件循环等核心数据,用于后续 API 调用(如 `ubus_subscribe`)[^1] | | `obj` | `struct ubus_object*` | 当前被调用的 ubus 对象指针,包含对象 ID、方法列表等元数据 | | `req` | `struct ubus_request_data*` | 请求元数据,用于异步响应管理(此同步函数未使用) | | `method` | `const char*` | **客户端请求的方法名**(在此例中值为 `"watch"`) | | `msg` | `struct blob_attr*` | **客户端传递的二进制参数**,需用 `blobmsg_parse` 解析(如示例中的 `{ "id": 123 }`)[^1][^4] | ### 关键代码解析 ```c // 解析客户端参数:msg -> tb[WATCH_ID] blobmsg_parse(watch_policy, __WATCH_MAX, tb, blob_data(msg), blob_len(msg)); uint32_t obj_id = blobmsg_get_u32(tb[WATCH_ID]); // 获取客户端传入的 ID // 使用 ctx 注册订阅 ubus_subscribe(ctx, &test_event, obj_id); ``` - 客户端参数通过 `msg` 传入,需按策略(`watch_policy`)解析为键值对(`tb[]`) - `ctx` 用于执行核心操作(如订阅事件)[^1] ### 调用流程示意图 ```mermaid sequenceDiagram participant Client as 客户端 participant Ubusd as ubusd 守护进程 participant Server as 服务端(test_watch) Client->>Ubusd: ubus call object_name watch '{"id":123}' Ubusd->>Server: 填充 ctx,obj,msg 等参数 Server->>Ubusd: 解析 msg→订阅事件 Ubusd->>Client: 返回操作结果 ```
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值