在openwrt的libubox中提供了blobmsg的库,这是一种什么样的消息呢?下面我们先了解一下下面几个概念。
Blob
二进制大对象,是“Binary Large Object”的缩写。
blob提供二进制数据处理能力。有几种支持的数据类型,并可以创建块数据在socket上发送。整型数字会在libubox库内部转换为网络字节序进行处理。
二进制块的处理方法是创建一个TLV(类型-长度-值)链表数据,支持嵌套类型数据,并提供设置和获取数据接口。blob定义在blob.h中。
blob用于二进制对象序列化;blob主要在一些系统级组件(ubox/libubox/ubus/netifd)内部使用,一般应用不需要使用blob封装,blob用数据结构blob_attr表示。
blob_buf用于管理(多个)二进制大对象。
![](https://img-blog.csdnimg.cn/img_convert/f5bd4d5a66b475e61075aa6111eaaa61.jpeg)
// blob_attr数据结构
struct blob_attr {
uint32_t id_len; //id占用最高字节,msb用于扩展,len占用低3个字节
char data[];
} __packed;
blob调用API
int main() { static struct blob_buf b; struct blob_attr *attr1,*attr2; char * str,*str1; void *ptr; struct blob_attr * pos; int rem=0; blob_buf_init(&b, 0);---初始化一块blob buf //设置blob数据 attr1=blob_put_string(&b, 1, "hello");---存入一个字符串 attr2=blob_put_u8(&b, 2, 100);------存储一个字节的数字 //获取blob数据 str=blob_get_string(attr1); int a1=blob_get_u8(attr2); //打印数据 printf("str=%s\n",str); printf("a1=%d\n",a1); //输出有效数据地址 和 id ptr=blob_data(attr1); // blob_data printf("ptr=%p\n",ptr); printf("id =%d",blob_id(attr1)); //blob_id rem=12; __blob_for_each_attr(pos,attr1,rem) //遍历 { str1=blob_get_string(pos); printf("str1 =%s\n",str1); } return 0; } |
blobmsg
blobmsg用于二进制对象网络序列化。嵌套在blob数据结构(blob_attr)的data区。因此形成:blob_buff <- blob_attr -< blobmsg,blob_buff可存储管理多个blob_attr,每个blob_attr又可存储管理一个blogmsg。且可存储在线性数据区,不需要链表指针。
blobmsg_policy用于解析和缓存blobmsg列表,一般声明为一个静态数组,用于指导消息解析。
blobmsg默认使用id为table。array类似C语言的数组,table类似C的结构。
![](https://img-blog.csdnimg.cn/img_convert/b5209485074d858a57e30fedd62e8e31.jpeg)
struct blobmsg_hdr {
uint16_t namelen;
uint8_t name[];
} __packed;
// 解析blobmsg列表
struct blobmsg_policy {
constchar *name; // 与blobmsg_hdr->name对应
enum blobmsg_type type; // 策略值的类型,数据打包解包时作为blob_attr id
};
blobmsg根节点是一个纯粹的blob,所以blobmsg解析时需要注意:
(1)第一层解析,data必须取值为blob_data(root_blob),len必须取值为blob_len(root_blob)
(2)第二层及以上解析,data必须取值为blobmsg_data(sub_blob),len必须取值为blobmsg_data_len(sub_blob)
所以,应避免混合使用blob和blobmsg语义,比如第一层使用blob语义,第二层使用blobmsg语义。
blobmsg_json
blobmsg_json用于json对象的序列化,json提供脚本级消息发送机制,如果应用需要脚本配合,则需要使用json。
示例
UCI配置文件: /etc/config/test
config policy test option name 'test' option enable '1' option dns '1.1.1.1 2.2.2.2' |
定义参数列表
enum { POLICY_ATTR_NAME, /** name */ POLICY_ATTR_ENABLE, /** enable */ POLICY_ATTR_DNS, /** dns */ __POLICY_ATTR_MAX }; staticconststruct blobmsg_policy policy_attrs[__POLICY_ATTR_MAX] = { [POLICY_ATTR_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING }, [POLICY_ATTR_ENABLE] = { .name = "enable", .type = BLOBMSG_TYPE_BOOL }, [POLICY_ATTR_DNS] = { .name = "dns", .type = BLOBMSG_TYPE_ARRAY }, }; /** 定义BLOBMSG_TYPE_ARRAY类型参数的实际数据类型 */staticconststruct uci_blob_param_info policy_attr_info[__POLICY_ATTR_MAX] = { [POLICY_ATTR_DNS] = { .type = BLOBMSG_TYPE_STRING }, }; staticconststruct uci_blob_param_list policy_attr_list = { .n_params = __POLICY_ATTR_MAX, .params = policy_attrs, .info = policy_attr_info, }; |
转化为blob
staticstruct uci_context *g_uci_ctx; staticstruct blob_buf *b; void transform(constchar *config) { struct uci_context *ctx = g_uci_ctx; struct uci_package *p = NULL; if (!ctx) { ctx = uci_alloc_context(); g_uci_ctx = ctx; uci_set_confdir(ctx, NULL); } else { p = uci_lookup_package(ctx, config); if (p) uci_unload(ctx, p); } if (uci_load(ctx, config, &p)) return; struct uci_element *e; struct blob_attr *config = NULL; uci_foreach_element(&p->sectons, e) { struct uci_section *s = uci_to_section(e); blob_buf_init(&b, 0); uci_to_blob(&b, s, &policy_attr_list); config = blob_memdup(b.head); /** * do something with `config` * free(config), when not use it */ } } |
使用转换后的blob
void foo(blob_attr *confg) { struct blob_attr *tb[__POLICY_ATTR_MAX]; blobmsg_parse(policy_attrs, __POLICY_ATTR_MAX, tb, blob_data(config), blob_len(config)); /** * do something with *tb[] */ } |