660 likes | 858 Views
ACL 库常用 API 使用介绍. 2011.10. 目录. 常用数据结构 网络编程 数据库编程 XML 解析库 JSON 解析库 HTTP 协议库 服务器编程. 常用数据结构. 动态数组 队列 堆栈 双向环 哈希表 二分块查找 二叉树 平衡二叉树 快速匹配树 通用遍历器. 动态数组(一) --- 常用数据结构. 结构定义: ACL_ARRAY 主要函数 : 数组创建: ACL_ARRAY* acl_array_create(int) 数组添加: int acl_array_append(ACL_ARRAY*, void*)
E N D
ACL库常用API使用介绍 2011.10
目录 • 常用数据结构 • 网络编程 • 数据库编程 • XML解析库 • JSON解析库 • HTTP协议库 • 服务器编程
常用数据结构 • 动态数组 • 队列 • 堆栈 • 双向环 • 哈希表 • 二分块查找 • 二叉树 • 平衡二叉树 • 快速匹配树 • 通用遍历器
动态数组(一)---常用数据结构 • 结构定义:ACL_ARRAY • 主要函数: • 数组创建:ACL_ARRAY* acl_array_create(int) • 数组添加:int acl_array_append(ACL_ARRAY*, void*) • 数组访问:acl_array_index(ACL_ARRAY*, int) • 数组释放:acl_array_free(ACL_ARRAY*, void (*)(void*)) • 举例: • ACL_ARRAY *a = acl_array_create(10); // 创建动态数组 • int i; • char *buf; • for (i = 0; i < 20; i++) { // 循环添加动态对象 • buf = malloc(256); • snprintf(buf, 256, “hello: %d”, i); • acl_array_append(a, buf); • } • for (i = 0; i < 20; i++) { // 查找定位 • char *ptr = (char*) acl_array_index(a, i); // 根据数组下标取出元素 • printf(“%s\n”, ptr); • } • acl_array_free(a, free); // 释放动态数组及动态对象 • 参考: lib_acl/include/stdlib/acl_array.h
字符串动态数组(二)---常用数据结构 • 结构定义:ACL_ARGV • 主要函数 • 创建:方法一,acl_argv_alloc(int),方法二,acl_argv_split(string) • 添加:acl_argv_add(ACL_ARGV*, string, …, NULL) • 访问:acl_argv_index(ACL_ARGV*, int) • 销毁:acl_argv_free(ACL_ARGV*) • 举例 • ACL_ARGV *a = acl_argv_split(“hello world, you are welcome; Bye!”, “ ,;!”); // 分隔字符串,并创建字符串数组 • int i, size; • acl_argv_add(a, “How”, “old”, “are”, “you”, NULL); // 向动态数组中添加新的字符串 • size = acl_argv_size(a); // 数组中元素个数 • for (i = 0; i < size; i++) { • char *ptr = acl_argv_index(a, i); // 取出对应下标的字符串 • printf(“%s\n”, ptr); • } • acl_argv_free(a); // 释放字符串数组 • 参考: lib_acl/include/stdlib/acl_argv.h
队列---常用数据结构 • 结构定义:ACL_FIFO • 主要函数 • 创建:acl_fifo_new() • 添加:向队列尾部添加,acl_fifo_push_back(ACL_FIFO*, void*) • 向队列头部添加,acl_fifo_push_front(ACL_FIFO*, void*) • 访问:访问队列头, (void*) acl_fifo_head(ACL_FIFO*);访问队列尾, (void*) acl_fifo_tail(ACL_FIFO*) • 弹出:从尾部弹出对象,(void*) acl_fifo_pop_front(ACL_FIFO*) ,与 acl_fifo_push_back 组合为先进先出方式 • 从头部弹出对象,(void*) acl_fifo_pop_back(ACL_FIFO*),与 acl_fifo_push_front 组合为后进先出方式 • 销毁:acl_fifo_free(ACL_FIFO*, void (*free_fn)(void*)) • 举例 • ACL_FIFO *fifo = acl_fifo_new(); // 创建队列 • char *buf, *ptr; • int i; • for (i = 0; i < size; i++) { • char *buf = malloc(256); • snprintf(buf, 256, “hello: %d”, i); • acl_fifo_push_back(fifo, buf); // 向队列中添加元素 • } • while ((ptr = (char*) acl_fifo_pop_front(fifo)) != NULL) { • printf(“>>ptr: %s\n”, ptr); • free(ptr); • } • acl_fifo_free(fifo, NULL); // 因为调用了 acl_fifo_pop_xxx 函数,已经将对象从队列中清除,所以不再需要输入 • // 用来释放队列中元素的回调函数 • 参考: lib_acl/include/stdlib/acl_fifo.h
双向环---常用数据结构 • 结构定义:ACL_RING • 主要函数 • 初始化:acl_ring_init(ACL_RING*) • 尾部添加:acl_ring_append(ACL_RING*, ACL_RING*) • 删除链结点:acl_ring_detach(ACL_RING*) • 弹出结点:(ACL_RING*) acl_ring_pop_head(ACL_RING*) • 结点转用户对象:acl_ring_to_appl(ACL_RING*, {app_type}, {ring_member}) • 遍历访问: acl_ring_foreach(ACL_RING_ITERATOR*, ACL_RING*) • 举例 • typedef struct DUMMY { • char buf[256]; • int n; • ACL_RING entry; • } DUMMY; • struct DUMMY dummy1, dummy2; • ACL_RING_ITERATOR iter; • ACL_RING header; • … • acl_ring_init(&header); // 初始化双向环头对象 • acl_ring_append(&header, &dummy1.entry); // 添加结点 • acl_ring_append(&header, &dummy2.entry); // 添加结点 • acl_ring_foreach(iter, &header) { // 遍历 • ACL_RING *item = iter.ptr; • struct DUMMY *dm = acl_ring_to_appl(item, DUMMY, entry); // 转换为用户类型 • … • } • acl_ring_detach(&dummy1.entry); // 将 dummy1 结点从双向环中删除掉 • 参考: lib_acl/include/stdlib/acl_ring.h
哈希表---常用数据结构 • 结构定义:ACL_HTABLE • 主要函数: • 创建:(ACL_HTABLE*) acl_htable_create(int init_size, unsigned int flag) • 添加:acl_htable_enter(ACL_HTABLE*, const char *key, void* obj) • 查询:(void*) acl_htable_find(ACL_HTABLE*, const char *key) • 销毁:acl_htable_free(ACL_HTABLE*, void (*free_fn)(void*)) • 举例: • ACL_HTABLE *table = acl_htable_create(10, 0); // 创建哈希表 • int i; • char *buf, key[256]; • for (i = 0; i < 20; i++) { // 循环添加动态对象 • snprintf(key, sizeof(key), “hello: %d”, i); • buf = malloc(256); • snprintf(buf, 256, “hello: %d”, i); • acl_htable_enter(table, key, buf); // 向哈希表中添加元素 • } • for (i = 0; i < 20; i++) { // 查找 • snprintf(key, sizeof(key), “hello: %d”, i); • buf = (char*) acl_htable_find(table, key); // 从哈希表中查找元素 • if (buf != NULL) • printf(“%s\n”, buf); • } • acl_htable_free(table, free); // 释放哈希表及动态成员变量 • 参考: lib_acl/include/stdlib/acl_htable.h
二分块查找---常用数据结构 • 结构定义:ACL_DLINK,ACL_DITEM • 主要函数: • 创建:(ACL_DLINK*) acl_dlink_create(int init_size) • 添加:acl_htable_enter(ACL_HTABLE*, const char *key, void* obj) • 查询:(void*) acl_htable_find(ACL_HTABLE*, const char *key) • 销毁:acl_htable_free(ACL_HTABLE*, void (*free_fn)(void*)) • 举例: • ACL_DLINK *dlink = acl_dlink_create(10); // 创建二分块查找对象 • // 添加多个整数区间 • acl_dlink_insert(dlink, 100, 100); • acl_dlink_insert(dlink, 85, 99); • acl_dlink_insert(dlink, 83, 85); • acl_dlink_insert(dlink, 1,50); • acl_dlink_insert(dlink, 50, 60); • acl_dlink_insert(dlink, 59, 80); • // 内部的排列应该为:1-80, 83-99, 100-100 • // 查找某个整数是否在 dlink 的整数区间内 • if (acl_dlink_lookup(dlink, 49) != NULL) • printf(“Yes, 49 in dlink range\r\n”); • acl_dlink_free(dlink); // 销毁过程 • 参考: lib_acl/include/stdlib/acl_dlink.h
平衡二叉树---常用数据结构 • 结构定义:avl_tree_t, avl_node_t, avl_index_t • 主要函数 • 创建:avl_create(avl_tree_t*, int (*cmp_fn)(const void*, const void*), sizeof(USER_STRUCT), offsetof(USER_STRUCT, node_member); • 添加:avl_add(avl_tree_t*, USER_STRUCT) • 查找:(USRE_STRUCT*) avl_find(avl_tree_t*, USER_STRUCT*, avl_index_t) • 删除:avl_remove(avl_tree_t*, USER_STRUCT*) • 销毁:avl_destroy(avl_tree_t*) • 举例 • typedef struct DUMMY { • char buf[256]; • int n; • avl_node_t entry; // 平衡二叉树结点 • } DUMMY; • static int cmp_func(const void* n1, const void* n2) // 平衡二叉树的比较函数 • { • DUMMY *d1 = (DUMMY*) n1, *d2 = (DUMMY*) n2; • Return (d1->n – d2->n); • } • … • struct DUMMY dummy1, dummy2, dummy, *dummy_ptr; • avl_tree_t tree; // 平衡二叉树对象 • avl_create(&tree, cmp_func, sizeof(DUMMY), offsetof(DUMMY, entry); // 创建平衡二叉树 • … • avl_add(&tree, &dummy1); // 添加结点 • avl_add(&tree, &dummy2); // 添加结点 • dummy.n = 1; • dummy_ptr = (DUMMY*) avl_find(&tree, &dummy); // 查找结点 • dummy.n = dummy1.n; • avl_remove(&tree, &dummy); // 删除结点 • dummy.n = dummy2.n; • avl_remove(&tree, &dummy); // 删除结点 • avl_destroy(&tree); // 销毁平衡二叉树对象 • 参考: lib_acl/include/stdlib/avl.h
通用遍历器---常用数据结构 • 结构定义:ACL_ITER • 使用方法:变量声明 正向遍历 逆向遍历 • ACL_ITER iter; acl_foreach(iter, container) { acl_foreach_reverse(iter, container) { void* ptr = iter.data; void* ptr = iter.data; … … • } } • 符合ACL_ITER要求的容器: • 数据结构部分:ACL_ARRAY, ACL_ARGV, ACL_FIFO, ACL_STACK, ACL_DLINK, ACL_HTABLE, ACL_BINHASH • 网络部分:ACL_DNS_DB, ACL_IFCONF • 数据库部分:ACL_SQL_RES, ZDB_STORE • 解析器:ACL_XML/ACL_XML_NODE, ACL_JSON/ACL_JSON_NODE • 举例: • typedef struct DUMMY { • char buf[256]; • int n; • } DUMMY; • ACL_ARRAY *a = acl_array_create(10); // 创建动态数组 • ACL_HTABLE *t = acl_htable_create(10, 0); // 创建哈希表 • DUMMY dummy1, dummy2, *dummy_ptr; • ACL_ITER iter; // 遍历对象 • acl_array_append(a, &dummy1); acl_array_append(a, &dummy2); // 添加结点至动态数组中 • acl_htable_enter(t, “key1”, &dummy1); acl_htable_enter(t, “key2”, &dummy2); // 添加结点至哈希表中 • acl_foreach(iter, a) { // 遍历动态数组中的所有元素 • dummy_ptr = (DUMMY*) iter.data; • printf(“%s\n”, dummy_ptr->buf); • } • acl_foreach(iter, t) { // 遍历哈希表中的所有元素 • dummy_ptr = (DUMMY*) iter.data; • printf(“%s\n”, dummy_ptr->buf); • } • acl_array_free(a, NULL); // 销毁动态数组 • acl_htable_free(t, NULL); // 销毁哈希表 • 参考: lib_acl/include/stdlib/acl_iterator.h
网络编程 • TCP/IP 协议基础 • 较为底层的网络编程 • TCP连接控制 • 较为高层的网络编程 • 网络流读写 • 域名解析 • 网络编程杂项 • 异步非阻塞网络编程
较为底层的网络编程 • 1)服务端 • 网络套接口监听:ACL_SOCKET acl_inet_listen(const char* addr, int backlog, int block_mode); • 接收客户端连接:ACL_SOCKET acl_inet_accept(ACL_SOCKET listenfd); • ACL_SOCKET acl_sane_accept(ACL_SOCKET listenfd, struct sockaddr *sa, socklen_t *len); • 示例: • const char* addr=“127.0.0.1:8080”; • ACL_SOCKET listenfd = acl_inet_listen(addr, 128, ACL_BLOCKING) /* 监听 */, clientfd; • acl_assert(listenfd); • while (1) { • clientfd = acl_inet_accept(listenfd); // 等待客户端连接 • if (clientfd == ACL_SOCKET_INVALID) • continue; • … • acl_socket_close(clientfd); // 关闭客户端连接 • } • 2)客户端 • 连接TCP服务器:ACL_SOCKET acl_inet_connect(const char* addr, int block_mode, int timeout); • 连接UNIX域服务器:ACL_SOCKET acl_unix_connect(const char* addr, int block_mode, int timeout); • int acl_sane_connect(ACL_SOCKET sock, const struct sockaddr* sa, socklen_t len); • 示例: • const char* addr = “127.0.0.1:8080”; • ACL_COCKET clientfd = acl_inet_connect(addr, ACL_BLOCKING, 60); // 连接服务器 • if (clientfd != ACL_SOCKET_INVALID) { • … • acl_socket_close(clientfd); // 关闭连接 • } • 参考:lib_acl/include/net, acl_listen.h, acl_connect.h
TCP连接控制 • 1)设置延迟发送开关(IPPROTO_TCP/TCP_NODELAY): • acl_tcp_nodelay(ACL_SOCKET fd, int onoff); • 2)设置数据可读接收(IPPROTO_TCP/ TCP_DEFER_ACCEPT): • void acl_tcp_defer_accept(ACL_SOCKET listenfd, int timeout); • 3)设置连接关闭属性(SOL_SOCKET/ SO_LINGER): • acl_tcp_so_linger(ACL_SOCKET fd, int onoff, int timeout); • 4)设置/获得套接口读缓冲区大小 • void acl_tcp_set_rcvbuf(ACL_SOCKET fd, int size); • int acl_tcp_get_rcvbuf(ACL_SOCKET fd); • 5)设置/获得套接口写缓冲区大小 • void acl_tcp_set_sndbuf(ACL_SOCKET fd, int size); • int acl_tcp_get_sndbuf(ACL_SOCKET fd); • 6)获得远程连接的地址 • int acl_getpeername(ACL_SOCKET sockfd, char *buf, size_t bsize); • 7)获得本地连接的地址 • int acl_getsockname(ACL_SOCKET sockfd, char *buf, size_t bsize); • 参考:lib_acl/include/net/acl_tcp_ctl.h
较为高层的网络编程 • 1)服务端: • 监听本地地址:ACL_VSTREAM *acl_vstream_listen(const char *addr, int qlen); • 接收远程连接:ACL_VSTREAM *acl_vstream_accept(ACL_VSTREAM *listen_stream, char *ipbuf, int bsize); • 示例: • const char* addr = “127.0.0.1:8080”; • ACL_VSTREAM* server = acl_vstream_listen(addr, 128) /* 服务器监听 */, *client; • acl_assert(server); • while (1) { • client = acl_vstream_accept(server, NULL, 0); // 等待客户端连接 • if (client == NULL) • continue; • do_something(client); • acl_vstream_close(client); // 关闭客户端连接 • } • 2)客户端: • 建立远程连接:ACL_VSTREAM *acl_vstream_connect(const char *addr, int block_mode, • int connect_timeout, int rw_timeout, int rw_bufsize); • 示例: • Const char* addr = “127.0.0.1:8080”; • ACL_VSTREAM* client = acl_vstream_connect(addr, ACL_BLOCKING, 60, 60, 8192); // 连接服务器 • If (client) { • do_something(client); • acl_vstream_close(client); // 关闭连接 • } • 参考: lib_acl/include/stdlib/acl_vstream_net.h
网络流读写接口 • 1)按行读数据,不去掉末尾的\r\n (阻塞方式) • int acl_vstream_gets(ACL_VSTREAM *stream, void *buf, size_t maxlen); • 2)按行读数据,去掉末尾的\r\n (阻塞方式) • int acl_vstream_gets_nonl(ACL_VSTREAM *stream, void *buf, size_t maxlen); • 3)读取N个字节的数据(阻塞方式) • int acl_vstream_readn(ACL_VSTREAM *stream, char *buf, int size); • 4)一次性从流读操作, 返回实际读取的字节数 • Int acl_vstream_read(ACL_VSTREAM *stream, char *buf, int size); • 5)写入N个字节的数据(阻塞方式) • int acl_vstream_writen(ACL_VSTREAM *stream, const void *vptr, size_t dlen); • 6)一次性写入流操作, 返回实际写入的字节数 • int acl_vstream_write(ACL_VSTREAM *stream, const void *vptr, size_t dlen); • 7)以格式方式写入数据(阻塞方式) • int acl_vstream_fprintf(ACL_VSTREAM *stream, const char *fmt, ...); • int acl_vstream_vfprintf(ACL_VSTREAM *stream, const char *fmt, va_list ap); • 8)带缓冲方式的写入(阻塞方式) • int acl_vstream_buffed_fprintf(ACL_VSTREAM *stream, const char *fmt, ...); • int acl_vstream_buffed_vfprintf(ACL_VSTREAM *stream, const char *fmt, va_list ap); • int acl_vstream_buffed_writen(ACL_VSTREAM *stream, const void *vptr, size_t dlen); • 9)刷新写缓冲区里的数据(阻塞方式) • int acl_vstream_fflush(ACL_VSTREAM *stream); • 参考:lib_acl/include/stdlib/acl_vstream.h
网络流写示例 • static void do_write(ACL_VSTREAM* client) • { • char buf[8192]; • const char* data = “hello world!\r\n”), *ptr; • int ret, n; • ACL_VSTREAM_SET_RWTIMO(client, 60); // 设置IO读写超时时间 • n = (int) strlen(data); • // 写数据过程 ret = acl_vstream_fprintf(client, “hello world\r\n%s”, data); // 格式写入一行数据 • if (ret == ACL_VSTREAM_EOF) { • acl_msg_error(“fprintf to client error: %s”, acl_last_serror()); • return; • } • ret = acl_vstream_writen(client, data, n); // 写入定长数据,完全写成功或出错时才返回 • if (ret == ACL_VSTREAM_EOF) { // 表示出错,之后应该关闭 client 连接流 • acl_msg_error(“write to client error: %s”, acl_last_serror()); • return; • } • // acl_vstream_writen 等效于如下写过程 • ptr = data; • while (n > 0) { • ret = acl_vstream_write(client, ptr, n); • if (ret == ACL_VSTREAM_EOF) { • acl_msg_error(“write to client error: %s”, acl_last_serror()); • return; • } • n -= ret; } • } • 参考:lib_acl/include/stdlib/acl_vstream.h; samples/vstream
网络流读示例 • static void do_read(ACL_VSTREAM* client) • { • char buf[8192]; • int ret; • ACL_VSTREAM_SET_RWTIMO(client, 60); // 设置IO读写超时时间 • ret = acl_vstream_gets(client, buf, sizeof(buf)); // 读完整一行数据,尾部含\r\n或\n • if (ret == ACL_VSTREAM_EOF) { • acl_msg_error(“%s(%d), %s: gets error: %s”, __FILE__, __LINE__, __FUNCTION__, acl_last_serror()); return; • } • ret = acl_vstream_gets_nonl(client, buf, sizeof(buf)); // 读完整行数据,尾部不含\r\n 或\n • if (ret == ACL_VSTREAM_EOF) { • acl_msg_error(“%s(%d), %s: gets error: %s”, __FILE__, __LINE__, __FUNCTION__, acl_last_serror()); return; • } • ret = acl_vstream_readn(client, buf, sizeof(buf) – 1); // 读 8191 个字节 • if (ret == ACL_VSTREAM_EOF) { • acl_msg_error(“%s(%d), %s: readn error: %s”, __FILE__, __LINE__, acl_last_serror()); return; • } • ret = acl_vstream_read(client, buf, sizeof(buf) - 1); // 只要有数据可读或出错或超时都返回 • if (ret == ACL_VSTREAM_EOF) { • acl_msg_error(“%s(%d), %s: read error: %s”, • __FILE__, __LINE__, acl_last_serror()); return; • } • acl_msg_info(“read over now”); • } • 参考:lib_acl/include/stdlib/acl_vstream.h; samples/vstream
域名解析 • 1)通用域名解析函数:ACL_DNS_DB *acl_gethostbyname(const char *name, int *h_error); • 2)释放解析结果内存:void acl_netdb_free(ACL_DNS_DB *dns_db); • 示例: • const char* domain = “mail.51iker.com”; • int errnum; • ACL_DNS_DB* dns_db = acl_gethostbyname(domain, &errnum); // 解析域名 • if (dns_db == NULL) { • printf(“can’t get %s’ ip, error: %s\r\n”, domain, acl_netdb_strerror(errnum)); • } else { • ACL_ITER iter; • acl_foreach(iter, dns_db) { // 遍历域名解析结果 • const ACL_HOSTNAME* name = (const ACL_HOSTNAME*) iter.data; • printf(“ip: %s\n”, name->ip); • } • acl_netdb_free(dns_db); • } • 参考:lib_acl/include/net/acl_netdb.h
网络编程杂项 • 1)从流中获得套接字:ACL_SOCKET fd = ACL_VSTREAM_SOCK(ACL_VSTREAM*); • 2)从流中获得远程地址:const char* addr = ACL_VSTREAM_PEER(ACL_VSTREAM*); • 3)设置流的读写超时时间:ACL_VSTREAM_SET_RWTIMO(ACL_VSTREAM*, int); • 4)获得本机网卡地址列表: • 结构定义:ACL_IFCONF, ACL_IFADDR • 函数定义: • 获得本机网卡集合:ACL_IFCONF *acl_get_ifaddrs(void); • 释放结果:void acl_free_ifaddrs(ACL_IFCONF *ifconf); • 示例: • ACL_IFCONF* ifconf = acl_get_ifaddrs(); // 获得本机的所有网卡地址 • If (ifconf) { • ACL_ITER iter; • acl_foreach(iter, ifconf) { // 遍历网卡地址集 • const ACL_IFADDR* addr = (const ACL_IFADDR*) iter.data; • printf(“name: %s, ip: %s\n”, addr->name, addr->ip); • } • acl_free_ifaddrs(ifconf); // 释放内存 • } • 参考:lib_acl/include/stdlib/acl_vstream.h, lib_acl/include/net/acl_ifconf.h
异步通信框架---非阻塞网络编程 • 1)结构定义:ACL_AIO, ACL_ASTREAM • 2)主要函数: • 创建异步句柄:ACL_AIO *acl_aio_create(int event_mode); • ACL_AIO *acl_aio_create2(int event_mode, unsigned int nMsg); • event_mode: ACL_EVENT_SELECT, ACL_EVENT_POLL, • ACL_EVENT_KERNEL(epoll/devpool/kqueue/iocp), ACL_EVENT_WMSG(win32) • 异步循环:void acl_aio_loop(ACL_AIO *aio); • 释放异步句柄:void acl_aio_free(ACL_AIO *aio); • 创建异步通信流:ACL_ASTREAM *acl_aio_open(ACL_AIO *aio, ACL_VSTREAM *stream); • 异步IO完成后关闭异步流:void acl_aio_iocp_close(ACL_ASTREAM *astream); • 设置异步流的参数:void acl_aio_ctl(ACL_ASTREAM *astream, int name, ...); • 主动检查延迟关闭连接:void acl_aio_check(ACL_AIO *aio); • 设置持续读状态:void acl_aio_set_keep_read(ACL_AIO *aio, int onoff); • 设置异步定时器:acl_int64 acl_aio_request_timer(ACL_AIO *aio, • ACL_AIO_TIMER_FN timer_fn, void *context, acl_int64 idle_limit); • 取消异步定时器:acl_int64 acl_aio_cancel_timer(ACL_AIO *aio, • ACL_AIO_TIMER_FN timer_fn, void *context); • 参考:lib_acl/include/aio/acl_aio.h
服务端接口 ---非阻塞网络编程 • 设置异步监听流的回调函数: • 方法一: • static int __accept_callback(ACL_ASTREAM *client, void *context) • { • do_something(client, context); • return 0; • } • acl_aio_ctl(ACL_ASTREAM *, ACL_AIO_CTL_ACCEPT_FN, __accept_callback, ACL_AIO_CTL_CTX, xxx, ACL_AIO_CTL_END); • 方法二: • static int __listen_callback(ACL_ASTREAM *sstream, void *context) • { • ACL_ASTREAM* client; • ACL_VSTREAM* cstream = acl_vstream_accept(acl_aio_vstream(sstream)); // 接收客户端连接 • If (cstream == NULL) • return (0); client = acl_aio_open(acl_aio_handle(sstream), cstream); // 将客户端连接转为异步流 do_something(client, context); return 0; • } • acl_aio_ctl(ACL_ASTREAM *, ACL_AIO_CTL_LISTEN_FN, __listen_callback, ACL_AIO_CTL_CTX, xxx, ACL_AIO_CTL_END); • 示例: • ACL_AIO* aio = acl_aio_create(ACL_EVENT_KERNEL); // 创建本机支持的内核级异步事件引擎对象 • ACL_ASTREAM* sstream; • ACL_VSTREAM* server = acl_vstream_listen(“127.0.0.1:8080”, 128); // 监听 • acl_assert(server); • sstream = acl_aio_open(aio, server); // 将监听流转为异步流 • acl_aio_ctl(sstream, ACL_AIO_CTL_ACCEPT_FN, __accept_callback, ACL_AIO_CTL_END); // 设置异步服务流的回调函数 • while (1) { • acl_aio_loop(aio); // 异步事件循环 • }
客户端接口—读操作 ---非阻塞网络编程 • 回调函数类型:typedef int (*ACL_AIO_READ_FN)(ACL_ASTREAM *astream, void *context, char *data, int dlen); • 主要函数: • 1)设置读回调函数(可以针对一个客户端同时添加多个回调函数): • void acl_aio_add_read_hook(ACL_ASTREAM *astream, ACL_AIO_READ_FN callback, void *ctx); • 2)删除读回调函数: • void acl_aio_del_read_hook(ACL_ASTREAM *astream, ACL_AIO_READ_FN callback, void *ctx); • 3)异步读一行数据: • void acl_aio_gets(ACL_ASTREAM *astream); • void acl_aio_gets_nonl(ACL_ASTREAM *astream); • 4)异步读规定字节数据: • void acl_aio_readn(ACL_ASTREAM *astream, int count); • 5)读任意长度的数据: • void acl_aio_read(ACL_ASTREAM *astream); • 示例: • static int __gets_callback(ACL_ASTREAM* client, void* ctx, char* line, int dlen) • { • printf(“gets: %s, len: %d\r\n”, line, dlen); • if (strcasecmp(line, “QUIT”) == 0) { • acl_aio_iocp_close(client); // 当IO完成后关闭流 • return -1; • } • return 0; • } • static void do_something(ACL_ASTREAM* client) • { • acl_aio_add_read_hook(client, __gets_callback, NULL); // 添加流的异步读回调,可以添加多个 • acl_aio_gets_nonl(client); // 异步读一行数据,末尾不带\r\n • }
客户端接口—写操作 --非阻塞网络编程 • 回调函数类型:typedef int (*ACL_AIO_WRITE_FN)(ACL_ASTREAM *astream, void *context); • 主要函数: • 1)添加异步写成功回调函数 • void acl_aio_add_write_hook(ACL_ASTREAM *astream, ACL_AIO_WRITE_FN callback, void *ctx); • 2)删除异步写回调函数 • void acl_aio_del_write_hook(ACL_ASTREAM *astream, • ACL_AIO_WRITE_FN callback, void *ctx); • 3)异步写指定长度数据 • void acl_aio_writen(ACL_ASTREAM *astream, const char *data, int dlen); • 4)类似 printf 格式异步写数据 • void acl_aio_fprintf(ACL_ASTREAM *astream, const char *fmt, ...); • void acl_aio_vfprintf(ACL_ASTREAM *astream, const char *fmt, va_list ap); • 5)异步写一组数据 • void acl_aio_writev(ACL_ASTREAM *astream, const struct iovec *vector, int count); • 示例: • static int __write_callback(ACL_ASTREAM* client, void* ctx) • { • printf(“write to peer ok\r\n”); • return (0); • } • static void do_something(ACL_ASTREAM* client) • { • acl_aio_add_write_hook(client, __write_callback, NULL); // 添加流异步写的回调函数,可添加多个 • acl_aio_writen(client, “hello\r\n”, sizeof(“hello\r\n”) – 1); // 异步写 • acl_aio_fprintf(client, “How are you\r\n”); // 异步写 • }
客户端接口—远程连接 --- 非阻塞网络编程 • 回调函数类型定义:typedef int (*ACL_AIO_CONNECT_FN)(ACL_ASTREAM *cstream, void *context); • 主要函数: • 1)添加连接成功回调函数 • void acl_aio_add_connect_hook(ACL_ASTREAM *astream, ACL_AIO_CONNECT_FN callback, void *ctx); • 2)删除连接成功回调函数 • void acl_aio_del_connect_hook(ACL_ASTREAM *astream, ACL_AIO_CONNECT_FN callback, void *ctx); • 3)异步连接 • ACL_ASTREAM *acl_aio_connect(ACL_AIO *aio, const char *saddr, int timeout); • 示例: • static int __connect_callback(ACL_ASTREAM* client, void* ctx) • { • do_somethine(client); • return 0; • } • static void test_connect(ACL_AIO* aio, const char* addr) • { • ACL_ASTREAM* client = acl_aio_connect(aio, addr, 10); // 异步连接服务器 • if (client == NULL) { • printf(“connect %s error(%s)\r\n”, addr, acl_last_serror()); • return; • } • acl_aio_add_connect_hook(client, __connect_callback, NULL); // 添加流异步连接的回调函数 • }
读写超时及关闭回调接口---非阻塞网络编程 • 回调函数类型: • 1)超时回调接口: • typedef int (*ACL_AIO_TIMEO_FN)(ACL_ASTREAM *astream, void *context); • 2)关闭回调接口: • typedef int (*ACL_AIO_CLOSE_FN)(ACL_ASTREAM *astream, void *context); • 主要函数: • 1)添加读写超时回调函数 • void acl_aio_add_timeo_hook(ACL_ASTREAM *astream, ACL_AIO_TIMEO_FN callback, void *ctx); • 2)删除超时回调函数 • void acl_aio_del_timeo_hook(ACL_ASTREAM *astream, ACL_AIO_TIMEO_FN callback, void *ctx); • 示例: • static int __timeout_callback(ACL_ASTREAM* client, void* ctx) • { • return -1; // 返回负值通知框架关闭该异步注连接,若返回 >= 0 则继续等待 • } • static int __close_callback(ACL_ASTREAM* client, void* ctx) • { • return 0; • } • static void set_callback(ACL_ASTREAM* client, int timeout) • { • acl_aio_ctl(client, ACL_AIO_CTL_TIMEOUT, timeout, • ACL_AIO_CTL_TIMEO_FN, __timeout_callback, ACL_AIO_CTL_END); • }
服务端例子 -- 非阻塞网络编程 • #include “lib_acl.h” • static int __stop = 0; • static int __accept_callback(ACL_ASTREAM* client, void* ctx) • { • acl_aio_add_read_hook(client, __read_callback, NULL); // 设置流的异步读回调函数 • acl_aio_add_write_hook(client, __write_callback, NULL); // 设置流的异步写回调函数 • acl_aio_gets_nonl(client); // 异步读一行数据,并且自动去掉末尾的 \r\n • return 0; • } • int main(int argc, const char* argv[]) • { • ACL_AIO* aio = acl_aio_create(ACL_EVENT_KERNEL); // 创建异步引擎 • ACL_ASTREAM* sstream; • ACL_VSTREAM* server = acl_vstream_listen(“127.0.0.1:8080”, 128); // 监听 • acl_assert(server); • sstream = acl_aio_open(aio, server); // 打开异步监听流 • acl_aio_ctl(sstream, ACL_AIO_CTL_ACCEPT_FN, __accept_callback, ACL_AIO_CTL_END); // 设置回调函数 • while (1) { • If (__stop) • break; • acl_aio_loop(aio); // 异步事件循环 • } • acl_aio_free(aio); // 释放异步事件引擎 • return 0; • }
服务端例子 ---非阻塞网络编程 • static int __read_callback(ACL_ASTREAM* client, void* ctx, char* data, int dlen) • { • printf(“gets: %s\n”, data); • If (strcasecmp(data, “quit”) == 0) { • acl_aio_iocp_close(client); // 关闭异步流 • Return -1; • } else if (strcasecmp(data, “stop”) == 0) { • __stop = 1; • return -1; • } • acl_aio_fprintf(client, “%s\r\n”, data); // 异步写一行数据 • return 0; • } • static int __write_callback(ACL_ASTREAM* client, void* ctx) • { • return 0; • } • static int __timeout_callback(ACL_ASTREAM* client, void* ctx) • { • printf(“client read timeout\r\n”); • return 0; • } • static int __close_callback(ACL_ASTREAM* client, void* ctx) • { • printf(“client close now\r\n”); • return 0; • }
客户端例子 ---非阻塞网络编程 • #include “lib_acl.h” • static int __stop = 0; • static int __connect_callback(ACL_ASTREAM* client, void* ctx) • { • static const char* mydata = “connection!”; • acl_aio_add_read_hook(client, __read_callback, NULL); // 设置异步读回调函数 • acl_aio_add_write_hook(client, __write_callback, NULL); // 设置异步写回调函数 • acl_aio_gets_nonl(client); // 异步读一行数据且不带 \r\n • acl_aio_fprintf(client, “hello world, %s\r\n”, mydata); // 异步写一行数据 • return 0; • } • int main(int argc, const char* argv[]) • { • const char* addr = “127.0.0.1:8080”; • ACL_AIO* aio = acl_aio_create(ACL_EVENT_SELECT); // 创建一个异步引擎 • ACL_ASTREAM* client; • int timeout = 10; • client = acl_aio_connect(aio, addr, timeout); // 异步连接服务器 • If (client == NULL) { • printf(“connect %s error(%s)\r\n”, addr, acl_last_serror()); • return 1; • } • while (1) { • If (__stop) • break; acl_aio_loop(aio); // 异步引擎循环 • } • acl_aio_free(aio); // 释放异步引擎 • return 0; • }
数据库编程 • 数据库连接池 • 数据库SQL操作
数据库连接池 • 数据结构类型:ACL_DB_POOL, ACL_DB_HANDLE, ACL_DB_INFO • 主要函数: • 1)创建连接池对象:ACL_DB_POOL *acl_dbpool_create(const char *db_type, const ACL_DB_INFO *db_info); • 2)销毁连接池对象:void acl_dbpool_destroy(ACL_DB_POOL *db_pool); • 3)从连接池获取连接对象:ACL_DB_HANDLE *acl_dbpool_peek(ACL_DB_POOL *db_pool); • 4)将连接对象转为实际连接对象:void *acl_dbpool_export(ACL_DB_HANDLE *db_handle); • 5)将连接对象归还连接池:void acl_dbpool_release(ACL_DB_HANDLE *db_handle); • 6)手动关闭连接对象:void acl_dbpool_close(ACL_DB_HANDLE *db_handle); • 示例: • 一、创建连接池对象,实际上并不连接数据库 • ACL_DB_POOL * dbconn_init(const char* dbaddr, const char* dbname, const char* dbuser, const char* dbpass, • int dbpool_max, int dbping, int dbtimeout) { ACL_DB_POOL *db_pool; ACL_DB_INFO db_info; //初始化连接池的参数 memset(&db_info, 0, sizeof(ACL_DB_INFO)); ACL_SAFE_STRNCPY(db_info.db_addr, dbaddr, sizeof(db_info.db_addr)); // 数据库地址,如:/tmp/mysql.sock 或 127.0.0.1:3306 ACL_SAFE_STRNCPY(db_info.db_name, dbname, sizeof(db_info.db_name)); //数据库名称 ACL_SAFE_STRNCPY(db_info.db_user, dbuser, sizeof(db_info.db_user)); // 数据库帐号 ACL_SAFE_STRNCPY(db_info.db_pass, dbpass, sizeof(db_info.db_pass)); // 数据库帐号密码 db_info.db_max = dbpool_max; // 最大连接个数 db_info.ping_inter = dbping; // 探测每个连接的时间间隔 db_info.timeout_inter = dbtimeout; // 每个连接的最长空闲时间 db_info.auto_commit = 1; // 自动提交修改过程 db_pool = acl_dbpool_create(“mysql”, &db_info); // 创建连接池 if (db_pool == NULL) acl_msg_fatal("%s(%d), %s: init db pool error", __FILE__, __LINE__, __FUNCTION__); return (db_pool); } 参考:lib_acl/include/db/acl_dbpool.h
数据库SQL操作 • 数据结构类型:ACL_SQL_RES • 主要函数: • 1)SQL查询:ACL_SQL_RES *acl_dbsql_select(ACL_DB_HANDLE *handle, • const char *sql, int *error); • 2)释放查询结果:void acl_dbsql_free_result(ACL_DB_HANDLE *handle, ACL_SQL_RES *res); • 3)SQL更新或添加:int acl_dbsql_update(ACL_DB_HANDLE *handle, const char *sql, int *error); • 示例: • 一、SQL查询过程 • static ACL_DB_POOL* __dbpool; // 需要提前创建连接池对象 • int sql_select_test(void) • { • const char* sql = “select name, age, pass from user_tbl where age >= 20”; • ACL_SQL_RES* result; // 查询结果集 • ACL_ITER iter; // 遍历指针 • int error = 0, n = 0; • ACL_DB_HANDLE* conn = acl_dbpool_peek(__dbpool); // 从连接池中获得数据库连接 • If (conn == NULL) { • acl_msg_error(“%s(%d), %s: get connection error”, __FILE__, __LINE__, __FUNCTION__); return -1; • } • result = acl_dbsql_select(conn, sql, &error); // 进行数据库SQL查询 • if (retsult == NULL) { • If (error != ACL_DB_OK && error != ACL_DB_ERR_EMPTY) { • acl_msg_error(“%s(%d), %s: sql select error: %d”, __FILE__, __LINE__, __FUNCTION__, error); • Acl_dbpool_close(conn); // 关闭连接 } else • acl_dbpool_release(conn); // 归还连接至连接池中 • return -1; • } • acl_dbpool_release(conn); // 归还连接至连接池中 • acl_foreach(iter, result) { // 遍历查询结果集 • const char** rows = (const char**) iter.data; • printf(“name: %s, age: %s, pass: %s\r\n”, rows[0], rows[1], rows[2]); • n++; • } • acl_dbsql_free_result(conn, res); // 释放查询结果集 • return n; • } • 参考:lib_acl/include/db/acl_dbsql.h
更新数据库操作---数据库编程示例 • 二、数据库 添加/修改/删除 过程 • static ACL_DB_POOL* __dbpool = NULL; // 需要提前创建连接池对象 • int sql_insert_test(void) • { • const char* sql = “insert into user(name, age, pass) values(‘myname’, 29, ‘hello’)”; • // const char* sql = “update user set age = 30 where name = ‘myname’”; • // const char* sql = “delete from user where name = ‘myname’”; • MYSQL* mysql; • int ret, error = 0, n; • ACL_DB_HANDLE* conn = acl_dbpool_peek(__dbpool); // 从连接池中获得数据库连接 • If (conn == NULL) { • acl_msg_error(“%s(%d), %s: get connection error”, __FILE__, __LINE__, __FUNCTION__); return -1; • } • ret = acl_dbsql_update(conn, sql, &error); // 更新数据库 • mysql = (MYSQL*) acl_dbpool_export(conn); // 从连接对象中导出 mysql 连接对象 • n = mysql_affected_rows(mysql); // 记录更新操作影响的行数 • acl_dbpool_release(conn); // 释放数据库连接,归还给连接池 • if (ret < 0) • acl_msg_error(“%s(%d), %s: update db error: %d”, __FILE__, __LINE__, __FUNCTION__, error); else acl_msg_info(“%s(%d), %s: affected: %d”, __FILE__, __LINE__, __FUNCTION__, n); return ret; • } • 参考:lib_acl/include/db/acl_dbsql.h, acl_dbpool.h
XML解析库 • 结构类型及常用API函数 • Xml 解析示例 • Xml 树遍历 • Xml 结点遍历 • Xml 结点查询 • Xml 结点属性查询
XML解析库 • 数据结构类型:ACL_XML, ACL_XML_NODE, ACL_XML_ATTR • 主要函数: • 1)创建 xml 解析器:ACL_XML *acl_xml_alloc(void); • 2)释放 xml 解析器:int acl_xml_free(ACL_XML *xml); • 3)将一个 xml 结点对象转换为xml解析器:void acl_xml_foreach_init(ACL_XML *xml, ACL_XML_NODE *node); • 4)重置 xml 解析器(在需要重复使用 xml 解析器时):void acl_xml_reset(ACL_XML *xml); • 5)设置 xml 解析器缓存(重复使用 xml 解析器提高效率):void acl_xml_cache(ACL_XML *xml, int max); • 6)流式方式解析 xml 数据:void acl_xml_update(ACL_XML *xml, const char *data); • 7)获得 xml 结点集合 • 7.1)ACL_ARRAY *acl_xml_getElementsByTagName(ACL_XML *xml, const char *tag); • 7.2)ACL_ARRAY *acl_xml_getElementsByTags(ACL_XML *xml, const char *tags); • 7.3)ACL_ARRAY *acl_xml_getElementsByAttr(ACL_XML *xml, const char *name, const char *value); • 7.4)ACL_ARRAY *acl_xml_getElementsByName(ACL_XML *xml, const char *value); • 8)根据ID获得 xml 结点:ACL_XML_NODE *acl_xml_getElementById(ACL_XML *xml, const char *id); • 9)获得 xml 结点中的属性对象 • 9.1)ACL_XML_ATTR *acl_xml_getElementAttr(ACL_XML_NODE *node, const char *name); • 9.2)ACL_XML_ATTR *acl_xml_getAttrById(ACL_XML *xml, const char *id); • 10)获得 xml 结点中属性值 • 10.1)const char *acl_xml_getElementAttrVal(ACL_XML_NODE *node, const char *name); • 10.2)ACL_XML_NODE *acl_xml_getElementById(ACL_XML *xml, const char *id); • 11)遍历: • 11.1)遍历 xml 树:acl_foreach(iter, ACL_XML*) {} • 11.2)遍历 xml 结点:acl_foreach(iter, ACL_XML_NODE*) {} • 参考:lib_acl/include/xml/acl_xml.h
Xml 解析示例 • #include “lib_acl.h” • static const char *__data = \ • "<root id='tt' name=‘xxxx’ value=‘xxxxx’ id=‘id_0001’>hello <root2> hi </root2></root><br/>\r\n"; • static void xml_parse_once(ACL_XML* xml) • { • acl_xml_update(xml, __data); // 更新 xml 解析器数据 • if (acl_xml_is_closure(xml)) // 检查 xml 解析器是否获得了完整的 xml 数据 • printf(“all xml data parse over\r\n”); else printf(“some xml data not update\r\n”); • } • static void xml_parse_part(ACL_XML* xml) • { • const char* ptr = __data; • char buf[2]; • while (*ptr) { • buf[0] = *ptr; • buf[1] = 0; • acl_xml_update(xml, buf); // 更新 xml 解析器数据,每次输入一个 xml 字节数据 } • } • int main(int argc acl_unused, char* argv[] acl_unused) • { • ACL_XML* xml = acl_xml_alloc(); // 创建 xml 解析器对象 • xml_parse_once(xml); // 一次性解析整个 xml 数据 • acl_xml_reset(xml); // 重置 xml 解析器状态,以备下一个解析过程 • xml_parse_part(xml); // 流式解析 xml 数据 • acl_xml_free(xml); // 释放 xml 解析器对象 • return 0; • } • 参考:lib_acl/include/xml/acl_xml.h, samples/xml
遍历 xml树 • void xml_iterator(ACL_XML* xml) • { • ACL_ITER iter1, iter2; • #define STR acl_vstring_str • #define LEN ACL_VSTRING_LEN /* 从根结点开始遍历 xml 对象的所有结点 */ acl_foreach(iter1, xml) { ACL_XML_NODE *node = (ACL_XML_NODE*) iter1.data; for (i = 1; i < node->depth; i++) { printf("\t"); } printf("tag> %s, text: %s\n", STR(node->ltag), STR(node->text)); /* 遍历 xml 结点的属性 */ acl_foreach(iter2, node->attr_list) { ACL_XML_ATTR *attr = (ACL_XML_ATTR*) iter2.data; for (i = 1; i < node->depth; i++) { printf("\t"); } printf("\tattr> %s: %s\n", STR(attr->name), STR(attr->value)); } } • }
遍历 xml 结点的第一级子结点 • void xml_node_iterator(ACL_XML* xml) • { • ACL_ITER iter1, iter2, iter3; • /* 遍历根结点的一级子结点 */ • acl_foreach(iter1, xml->root) { • ACL_XML_NODE *node = (ACL_XML_NODE*) iter1.data; • printf("tag> %s, text: %s\n", STR(node->ltag), STR(node->text)); • /* 遍历一级子结点的一级子结点 */ • acl_foreach(iter2, node) { • ACL_XML_NODE *node2 = (ACL_XML_NODE*) iter2.data; • printf("\ttag> %s, text: %s\n", STR(node2->ltag), STR(node2->text)); • /* 遍历一级子结点的属性 */ • acl_foreach(iter3, node2->attr_list) { • ACL_XML_ATTR *attr = (ACL_XML_ATTR*) iter3.data; • printf("\t\tattr> %s: %s\n", STR(attr->name), STR(attr->value)); • } • } • } • }
遍历 xml 结点以及所有的子结点 • void xml_node_iterator_all(ACL_XML_NODE* node) • { • ACL_XML xml; • ACL_ITER iter1, iter2; • #define STR acl_vstring_str • #define LEN ACL_VSTRING_LEN • acl_xml_foreach_init(&xml, node); // 将 xml 结点转为 xml 树对象 /* 从根结点开始遍历 xml 对象的所有结点 */ acl_foreach(iter1, xml) { ACL_XML_NODE *node = (ACL_XML_NODE*) iter1.data; for (i = 1; i < node->depth; i++) { printf("\t"); } printf("tag> %s, text: %s\n", STR(node->ltag), STR(node->text)); /* 遍历 xml 结点的属性 */ acl_foreach(iter2, node->attr_list) { ACL_XML_ATTR *attr = (ACL_XML_ATTR*) iter2.data; for (i = 1; i < node->depth; i++) { printf("\t"); } printf("\tattr> %s: %s\n", STR(attr->name), STR(attr->value)); } } • }
xml 结点查询 • 一、根据标签名获得 xml 结点集合 • void xml_node_lookup1(ACL_XML* xml) • { • ACL_ITER iter; • ACL_ARRAY* a; • a = acl_xml_getElementsByTagName(xml, "user"); • if (a) { • /* 遍历结果集 */ • acl_foreach(iter1, a) { • ACL_XML_NODE *node = (ACL_XML_NODE*) iter1.data; • printf("tag> %s, text: %s\n", STR(node->ltag), STR(node->text)); • } • /* 释放数组对象 */ • acl_xml_free_array(a); • } • } • 二、查询属性名为 name, 属性值为 user2_1 的所有 xml 结点的集合 • void xml_node_lookup2(ACL_XML* xml) • { • ACL_ITER iter; • ACL_ARRAY* a; • a = acl_xml_getElementsByName(xml, "user2_1"); • if (a) { • /* 遍历结果集 */ • acl_foreach(iter1, a) { • ACL_XML_NODE *node = (ACL_XML_NODE*) iter1.data; • printf("tag> %s, text: %s\n", STR(node->ltag), STR(node->text)); • } • /* 释放数组对象 */ • acl_xml_free_array(a); • } • }
XML 结点查询 • 三、从 xml 对象中获得所有的与给定多级标签名相同的 xml 结点的集合 • void xml_node_lookup3(ACL_XML* xml) • { • ACL_ARRAY* a; • const char* tags = “root/root2”; • a = acl_xml_getElementsByTags(xml, tags); • if (a) { • ACL_ITER iter; • acl_foreach(iter, a) { • ACL_XML_NODE* node = (ACL_XML_NODE*) iter.data; • … • } • acl_xml_free_array(a); • } • } • 四、从 xml 对象中获得指定 id 值的 xml 结点元素 • void xml_node_lookup4(ACL_XML* xml) • { • #define STR acl_vstring_str • ACL_XML_NODE* node = acl_xml_getElementById(xml, "id2_3"); • if (node) { • printf("-------------- walk %s node -------------------\n", STR(pnode->ltag)); • /* 遍历该 xml 结点的属性 */ • acl_foreach(iter1, pnode->attr_list) { • ACL_XML_ATTR *attr = (ACL_XML_ATTR*) iter1.data; • printf("\tattr_name: %s, attr_value: %s\n", • STR(attr->name), STR(attr->value)); • } • } • }
Xml 结点属性查询 • static void xml_node_attr(ACL_XML_NODE* node) • { • Const char* ptr; • ACL_XML_ATTR* attr; • ptr = acl_xml_getElementAttrVal(node, “name”); // 根据属性名获得属性值 • if (ptr) • printf(“name: %s\r\n”, ptr); attr = acl_xml_getAttrValueById(node->xml, “id_001”); // 根据属性唯一ID号获得属性对象 if (attr) printf(“id: %s\n”, acl_vstring_str(attr->value)); attr = acl_xml_getElementAttr(node, “name”); // 根据属性名获得属性对象 if (attr) printf(“id: %s\n”, acl_vstring_str(attr->value)); • }
JSON解析 • 结构类型及常用API函数 • json 解析示例 • json 树遍历 • json 结点遍历 • json 结点查询
结构类型及常用API函数---JSON解析 • 数据结构类型:ACL_JSON, ACL_JSON_NODE • 主要函数: • 1)创建 json 解析器对象:ACL_JSON *acl_json_alloc(void); • 2)释放 json 解析器对象:int acl_json_free(ACL_JSON *json); • 3)将 json 结点转换为 json 树对象:void acl_json_foreach_init(ACL_JSON *json, ACL_JSON_NODE *node); • 4)启用 json 解析器的缓存机制:void acl_json_cache(ACL_JSON *json, int max_cache); • 5)重置 json 解析器状态:void acl_json_reset(ACL_JSON *json); • 6)流式方式给 json 解析器输入数据:void acl_json_update(ACL_JSON *json, const char *data); • 7)获得 json 结点集 • 7.1)根据标签名获得:ACL_ARRAY *acl_json_getElementsByTagName(ACL_JSON *json, const char *tag); • 7.2)根据多级标签名获得: • ACL_ARRAY *acl_json_getElementsByTags(ACL_JSON *json, const char *tags); • 8)释放 json 结点集:void acl_json_free_array(ACL_ARRAY *a); • 参考:lib_acl/include/stdlib/acl_json.h
json 解析示例 • static const char* default_data = \ • "{'menu': {\r\n" • " 'id': 'file',\r\n" • " 'value': 'File',\r\n" • " 'popup': {\r\n" • " 'menuitem': [\r\n" • " {'value': 'New', 'onclick': 'CreateNewDoc()'},\r\n" • " {'value': 'Open', 'onclick': 'OpenDoc()'},\r\n" • " {'value': 'Close', 'onclick': 'CloseDoc()'}\r\n" • " ],\r\n" • " 'menuname': 'hello world'\r\n" • " }\r\n" • "}}\r\n"; • static void test_json_data(const char* data) • { • ACL_JSON* json = acl_json_alloc(); // 创建 json 解析器 • const char* ptr = data; • char buf[2]; • while (*ptr) • { • buf[0] = *ptr++; • buf[1] = 0; • acl_json_update(json, buf); // 流式方式输入 json 数据 • if (json->finish) • break; • } • acl_json_free(json); // 释放 json 解析器对象 • }
json 树遍历 • static void test_json_foreach(ACL_JSON* json) • { • ACL_ITER iter; • #define STR acl_vstring_str • acl_foreach(iter, json) // 遍历 json 所有结点 • { • const ACL_JSON_NODE* node = (const ACL_JSON_NODE*) iter.data; • for (int i = 1; i < node->depth; i++) // 根据结点层级输出 tab • printf("\t"); • printf("tag> %s, %s, text: %s, child: %s, type: %s\n", • STR(node->ltag), STR(node->parent->ltag), • STR(node->text), node->tag_node ? "yes" : "no", • acl_json_node_type(node)); • } • }
json 结点遍历 • static void test_json_find1(ACL_JSON* json) • { • const char* tags = "menu/popup/menuitem"; • ACL_ARRAY* a = acl_json_getElementsByTags(json, tags); // 获得结点对象集 • ACL_ITER iter1, iter2, iter3; • if (a == NULL) • return; • printf(">>find: %s\r\n", tags); • acl_foreach(iter1, a) { • ACL_JSON_NODE* node1 = (ACL_JSON_NODE*) iter1.data; • printf("%s: %s\r\n", tags, STR(node1->text)); • acl_foreach(iter2, node1) { • ACL_JSON_NODE* node2 = (ACL_JSON_NODE*) iter2.data; • acl_foreach(iter3, node2) { • ACL_JSON_NODE* node3 = (ACL_JSON_NODE*) iter3.data; • printf("\t%s: %s\r\n", STR(node3->ltag), STR(node3->text)); • } • printf("---------------------------------------------------\r\n"); • } • } • printf(">>find %s end\r\n", tags); • acl_json_free_array(a); • }
json 结点查询 • static void test_json_find2(ACL_JSON* json) • { • const char* tag = "onclick"; • ACL_ITER iter; • ACL_ARRAY* a = acl_json_getElementsByTagName(json, tag); • if (a == NULL) • return; • acl_foreach(iter, a) { • ACL_JSON_NODE* node = (ACL_JSON_NODE*) iter.data; • printf("find %s result: %s\r\n", tag, STR(node->text)); • } • acl_json_free_array(a); • } • static void test_json_find1(ACL_JSON* json) • { • const char* tags = "menu/popup/menuname"; • ACL_ARRAY* a = acl_json_getElementsByTags(json, tags); • ACL_ITER iter; • if (a == NULL) • return; • acl_foreach(iter, a) { • ACL_JSON_NODE* node = (ACL_JSON_NODE*) iter.data; printf("find %s result: %s\r\n", tag, STR(node->text)); • } • acl_json_free_array(a); • }