Linux高并发网络编程开发——xml json
在学习Linux高并发网络编程开发总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。
10-Linux系统编程-第16天(xml json)
目录:
一、学习目标
二、libevent复习
1、libevent整体工作流程回顾
2、libevent socket服务器复习
3、libevent socket客户端代码实现
4、bug
三、xml
1、xml文件格式
2、xml示例文件
3、xml文件的设计
4、mxml安装和配置
5、mxml生成文件函数介绍
6、使用mxml api生成xml文件
7、mxml获取文件数据
8、mxml格式文件解析
四、json
1、json文件格式
2、json格式示例文件
3、cjson介绍
4、cjson api创建json文件
5、cjson解析api
》附:QT中的json类
一、学习目标
xml
- xml基础语法和规范
- C程序如何使用xml开源库
- 借助开源库,在C程序中生成xml文件
- 已知一个xml文件,如何借助开源库解析xml文件数据
json
- json的基础语法和规范
- C程序中如何使用json开源库-cjson
- 使用cjson生成json文件
- 已知一个json文件,使用cjson库解析文件数据
二、libevent复习
1、libevent整体工作流程回顾
2、libevent socket服务器复习
3、libevent socket客户端代码实现
>touch client.c
>vi client.c
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <stdlib.h> 4 #include <sys/types.h> 5 #include <sys/stat.h> 6 #include <string.h> 7 #include <event2/event.h> 8 #include <event2/bufferevent.h> 9 #include <arpa/inet.h> 10 11 // 读回调 12 void read_cb(struct bufferevent *bev, void *arg) 13 { 14 // 接收对方发送过来的数据 15 char buf[1024] = {0}; 16 int len = bufferevent_read(bev, buf, sizeof(buf)); 17 printf("recv data: %s\n", buf); 18 19 // 给对方发数据 20 bufferevent_write(bev, buf, len+1); 21 printf("数据已经发送完成...\n"); 22 } 23 24 // 写回调 25 void write_cb(struct bufferevent *bev, void *arg) 26 { 27 printf("我是一个没有什么卵用的函数....\n"); 28 } 29 30 // 事件回调 31 void event_cb(struct bufferevent *bev, short events, void *arg) 32 { 33 if (events & BEV_EVENT_EOF) 34 { 35 printf("connection closed\n"); 36 } 37 else if(events & BEV_EVENT_ERROR) 38 { 39 printf("some other error\n"); 40 } 41 else if(events & BEV_EVENT_CONNECTED) 42 { 43 printf("成功连接到服务器, O(∩_∩)O哈哈~\n"); 44 return; 45 } 46 47 // 释放资源 48 bufferevent_free(bev); 49 printf("free bufferevent...\n"); 50 } 51 52 // 终端接受输入,将数据发给server 53 void read_terminal(evutil_socket_t fd, short what, void *arg) 54 { 55 // 读终端中的数据 56 char buf[1024] = {0}; 57 int len = read(fd, buf, sizeof(buf)); 58 59 // 将数据发送给server 60 struct bufferevent* bev = (struct bufferevent*)arg; 61 printf("请输入要发送的数据: \n"); 62 bufferevent_write(bev, buf, len+1); 63 } 64 65 66 int main(int argc, const char* argv[]) 67 { 68 // init server info 69 struct sockaddr_in serv; 70 memset(&serv, 0, sizeof(serv)); 71 serv.sin_family = AF_INET; 72 serv.sin_port = htons(9876); 73 evutil_inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr); 74 75 //创建事件处理框架 76 struct event_base* base; 77 base = event_base_new(); 78 79 //创建事件 80 // 连接服务器 -- fd 81 int fd = socket(AF_INET, SOCK_STREAM, 0); 82 //fd - bufferevent 83 struct bufferevent* bev; 84 bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE); 85 86 // 连接服务器 87 bufferevent_socket_connect(bev, (struct sockaddr*)&serv, sizeof(serv)); 88 89 // 给缓冲区设置回调 90 bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL); 91 bufferevent_enable(bev, EV_READ | EV_PERSIST); 92 93 // 接收键盘输入 94 // 创建一个事件 95 struct event* ev = event_new(base, STDIN_FILENO, 96 EV_READ | EV_PERSIST, 97 read_terminal, bev); 98 //添加事件到event_base 99 event_add(ev, NULL); 100 101 //启动事件循环 102 event_base_dispatch(base); 103 104 //释放资源 105 event_base_free(base); 106 107 return 0; 108 }
>gcc client.c -o client -levent
>./server
(打开另一个终端,先把之前的客户端拷贝至bufferevent目录下,然后编译后运行./client 127.0.0.1 9876;然后发送数据,查看原server终端的接收情况)
》报错:客户端无限写?
4、bug
》原因及解决:读回调中read_cb给终端发送数据bufferevent_write去掉,因为终端接受输入,将数据发给server的read_terminal已经发送了数据。所以将read_cb函数中bufferevent_write(bev, buf, len+1);注释掉。
三、xml
1、xml文件格式
>test.xml
1 <!-- 注释 --> 2 <!-- 文件头 --> 3 <?xml version="1.0" encoding="utf-8"?> 4 <!-- 树状结构的标签 --> 5 <!-- 根标签 -- 自个起名 只有一个 --> 6 <!-- 便签的值区分大小写 --> 7 8 9 <!-- 标签一般成对出现 --> 10 <Zhangsan> 11 <!-- 不成对--> 12 <lisi/> 13 <!-- 子标签 --> 14 <child> 15 <!-- 没有子标签,标签必须有值 --> 16 <!-- 标签设置属性 report:属性,属性值需要使用""包含 --> 17 <name report = "yes">zhang1</name> 18 <age month="1">12</age> 19 <sex>男</sex> 20 </child> 21 <!-- 子标签 --> 22 <child> 23 <name>zhang2</name> 24 <age>4</age> 25 <sex>女</sex> 26 </child> 27 </Zhangsan>
2、xml示例文件
>car.xml
1 <?xml version="1.0" encoding="utf-8"?> 2 <car> 3 <factory name="一汽大众"> 4 <brand> 5 <name>高尔夫</name> 6 <color>红色</color> 7 <price>15万</price> 8 </brand> 9 <brand> 10 <name>速腾</name> 11 <color>银白</color> 12 <price>18万</price> 13 </brand> 14 <brand> 15 <name>迈腾</name> 16 <color>黑灰</color> 17 <price>28万</price> 18 </brand> 19 </factory> 20 <factory name="上海大众"> 21 <brand> 22 <name>帕萨特</name> 23 <color>黑色</color> 24 <price>25万</price> 25 </brand> 26 <brand> 27 <name>POLO</name> 28 <color>灰色</color> 29 <price>8万</price> 30 </brand> 31 </factory> 32 </car>
3、xml文件的设计
>china.xml(改为石家庄)
1 <?xml version="1.0" encoding="utf-8"?> 2 <China> 3 <City> 4 <Name isbig="true">北京</Name> 5 <Area>1.641万平方千米</Area> 6 <Population>2200万</Population> 7 </City> 8 <City> 9 <Name isbig="false">石家庄</Name> 10 <Area>15848平方千米</Area> 11 <Population>107万</Population> 12 </City> 13 </China>
4、mxml安装和配置
》源码安装——安装文件:mxml-2.10.tar
>tar xvf mxml-2.10.tar
>cd msml-2.10
>ls
>vi README
>./configure
>make
>sudo make install
》测试是否安装成功
>ls (查看是否有testmxml.c)
>gcc testmxml.c -lmxml -lpthread -o testmxml
>./testmxml test.xml
(查看test.xml文件是否原样输出)
》问题:编译为什么需要加-lpthread?
答:安装时实现需要用到线程库,可以避免,安装时采用./configure --enable-threads=no
》./configure --enable-threads=no && make 是什么意思?
答:一句话两个命令,第一条执行成功,执行第二条
5、mxml生成文件函数介绍
》开源库minixml的使用:
6、使用mxml api生成xml文件
>touch create_xml.c
>vi create_xml.c
1 #include <stdio.h> 2 #include <mxml.h> 3 4 int main(int argc, const char* argv[]) 5 { 6 // 创建xml文件头节点 7 mxml_node_t *xml = mxmlNewXML("1.0"); 8 9 // 创建xml根节点 - china 10 mxml_node_t* china = mxmlNewElement(xml, "China"); 11 12 13 // 创建城市节点 14 mxml_node_t* city = mxmlNewElement(china, "City"); 15 // 添加子节点 16 // name 17 mxml_node_t* name = mxmlNewElement(city, "Name"); 18 // 设置标签值 19 mxmlNewText(name, 0, "北京"); 20 mxmlElementSetAttr(name, "isbig", "true"); 21 // 面积 22 mxml_node_t* area = mxmlNewElement(city, "Area"); 23 mxmlNewText(area, 0, "1.641万平方千米"); 24 // 人口 25 mxml_node_t* popu = mxmlNewElement(city, "Population"); 26 mxmlNewText(popu, 0, "2200万"); 27 28 // 第二个城市节点 29 city = mxmlNewElement(china, "City"); 30 // name 31 name = mxmlNewElement(city, "Name"); 32 mxmlNewText(name, 0, "石家庄"); 33 mxmlElementSetAttr(name, "isbig", "false"); 34 area = mxmlNewElement(city, "Area"); 35 mxmlNewText(area, 0, "15848平方千米"); 36 popu = mxmlNewElement(city, "Population"); 37 mxmlNewText(popu, 0, "107万"); 38 39 // 将xml内容保存到磁盘 40 FILE* fp = fopen("china.xml", "w"); 41 mxmlSaveFile(xml, fp, MXML_NO_CALLBACK); 42 fclose(fp); 43 mxmlDelete(xml); 44 45 return 0; 46 }
>gcc create_xml.c -lmxml -lpthread -o create
>./create
>cat china.xml
问题:创建的China.xml文件的格式默认写在一行,看着非常不直观?
答:可以使用XMLView.exe看,将china.xml拖拽进去后即可。另外XMLView.exe还有“容错”功能,勾选“Options”->“Strict XML parsing”可以自动识别错误并改正。
7、mxml获取文件数据
8、mxml格式文件解析
》练习解析car.xml
》文件准备—car.xml,car2.xml
>car.xml
1 <?xml version="1.0" encoding="utf-8"?> 2 <car> 3 <factory name="一汽大众"> 4 <brand> 5 <name>高尔夫</name> 6 <color>红色</color> 7 <price>15万</price> 8 </brand> 9 <brand> 10 <name>速腾</name> 11 <color>银白</color> 12 <price>18万</price> 13 </brand> 14 <brand> 15 <name>迈腾</name> 16 <color>黑灰</color> 17 <price>28万</price> 18 </brand> 19 </factory> 20 <factory name="上海大众"> 21 <brand> 22 <name>帕萨特</name> 23 <color>黑色</color> 24 <price>25万</price> 25 </brand> 26 <brand> 27 <name>POLO</name> 28 <color>灰色</color> 29 <price>8万</price> 30 </brand> 31 </factory> 32 </car>
>car2.xml
1 <?xml version="1.0" encoding="utf-8"?><car><factory name="一汽大众"><brand><name>高尔夫</name><color>红色</color><price>15万</price></brand><brand><name>速腾</name><color>银白</color><price>18万</price></brand><brand><name>迈腾</name><color>黑灰</color><price>28万</price></brand></factory><factory name="上海大众"><brand><name>帕萨特</name><color>黑色</color><price>25万</price></brand><brand><name>POLO</name><color>灰色</color><price>8万</price></brand></factory></car>
》练习—解析car.xml:
>touch parsecar.c
>vi parsecar.c
1 #include <stdio.h> 2 #include "mxml.h" 3 4 int main(int argc, const char* argv[]) 5 { 6 if(argc < 2) 7 { 8 printf("./a.out filename\n"); 9 return 0; 10 } 11 // 加载xml文件 12 FILE* fp = fopen(argv[1], "r"); 13 mxml_node_t* root = mxmlLoadFile(NULL, fp, MXML_NO_CALLBACK); 14 15 // 找到第一个factory节点 16 mxml_node_t* factory = mxmlFindElement(root, root, "factory", "name", NULL, MXML_DESCEND); 17 // 循环查找 18 while( factory ) 19 { 20 // 打印几点的属性值 21 printf("factory attr: %s\n", mxmlElementGetAttr(factory, "name")); 22 23 // 向下移动一个节点 24 mxml_node_t* brand = mxmlWalkNext(factory, root, MXML_DESCEND); 25 while( brand ) 26 { 27 // name 28 mxml_node_t* node = mxmlWalkNext(brand, root, MXML_DESCEND_FIRST); 29 printf(" name: %s\n", mxmlGetText(node, 0)); 30 // color 31 node = mxmlWalkNext(node, root, MXML_NO_DESCEND); 32 printf(" color: %s\n", mxmlGetText(node, 0)); 33 // price 34 node = mxmlWalkNext(node, root, MXML_NO_DESCEND); 35 printf(" price: %s\n", mxmlGetText(node, 0)); 36 printf(" =========================================\n"); 37 38 // 找到下一个品牌节点 39 brand = mxmlFindElement(brand, root, "brand", NULL, NULL, MXML_NO_DESCEND); 40 } 41 // 打印该厂家对应的车辆品牌和属性信息 42 // 查找下一个节点 43 factory = mxmlFindElement(factory, root, "factory", "name", NULL, MXML_NO_DESCEND); 44 } 45 mxmlDelete(root); 46 fclose(fp); 47 48 return 0; 49 }
>gcc parsecar.c -lmxml -o parse
>./parse car.xml
问题:(./parse car.xml)解析错误,名字对不上?而./parse car2.xml)解析正确?
答:函数mxmlWalkNext有bug,有换行会出问题,慎用!!!最好选用mxmlFindElement。
四、json
1、json文件格式
》注意:
1)不论是json对象还是json数组,json格式的文件中对象或数组最后一个元素后都没有逗号 !
2)json根对象或json根数组只能有一个。
2、json格式示例文件
>login.json
1 { 2 "KSF": "康师傅", 3 "Kevin": "Apple", 4 "Leilei": "loveHanMeiMei", 5 "Licy": "red", 6 "Lucy": "HelloWorld" 7 }
>brand.json
1 { 2 "奔驰": { 3 "factory": "一汽大众", 4 "last": 31, 5 "price": 83, 6 "sell": 49, 7 "sum": 80, 8 }, 9 "奥迪A6": { 10 "factory": "一汽大众", 11 "last": 15, 12 "price": 36, 13 "sell": 35, 14 "sum": 50 15 }, 16 "宝来": { 17 "factory": "一汽大众", 18 "last": 75, 19 "price": 41, 20 "sell": 5, 21 "sum": 80 22 }, 23 "富康": { 24 "factory": "二汽神龙", 25 "last": 38, 26 "price": 28, 27 "sell": 22, 28 "sum": 60 29 }, 30 "帕萨特": { 31 "factory": "上海大众", 32 "last": 42, 33 "price": 27, 34 "sell": 23, 35 "sum": 65 36 }, 37 "捷达": { 38 "factory": "一汽大众", 39 "last": 69, 40 "price": 10, 41 "sell": 11, 42 "sum": 80 43 }, 44 "标致307": { 45 "factory": "二汽神龙", 46 "last": 58, 47 "price": 27, 48 "sell": 12, 49 "sum": 70 50 }, 51 "桑塔纳": { 52 "factory": "上海大众", 53 "last": 63, 54 "price": 25, 55 "sell": 12, 56 "sum": 75 57 }, 58 "毕加索": { 59 "factory": "二汽神龙", 60 "last": 29, 61 "price": 39, 62 "sell": 21, 63 "sum": 50 64 } 65 }
>saleList.json
1 { 2 "2016-07-18": { 3 "02:17:51": { 4 "brand": "奔驰", 5 "factory": "一汽大众", 6 "number": 1, 7 "sum": 83 8 }, 9 "02:17:54": { 10 "brand": "奔驰", 11 "factory": "一汽大众", 12 "number": 1, 13 "sum": 83 14 }, 15 "02:17:59": { 16 "brand": "富康", 17 "factory": "二汽神龙", 18 "number": 2, 19 "sum": 56 20 }, 21 "02:22:37": { 22 "brand": "奔驰", 23 "factory": "一汽大众", 24 "number": 1, 25 "sum": 83 26 }, 27 "02:22:40": { 28 "brand": "奔驰", 29 "factory": "一汽大众", 30 "number": 1, 31 "sum": 83 32 }, 33 "09:08:36": { 34 "brand": "富康", 35 "factory": "二汽神龙", 36 "number": 2, 37 "sum": 56 38 }, 39 "09:17:22": { 40 "brand": "奔驰", 41 "factory": "一汽大众", 42 "number": 3, 43 "sum": 249 44 } 45 }, 46 "2016-07-19": { 47 "09:27:24": { 48 "brand": "富康", 49 "factory": "二汽神龙", 50 "number": 1, 51 "sum": 28 52 }, 53 "11:05:17": { 54 "brand": "标致307", 55 "factory": "二汽神龙", 56 "number": 2, 57 "sum": 54 58 } 59 }, 60 "2016-09-25": { 61 "10:07:24": { 62 "brand": "帕萨特", 63 "factory": "上海大众", 64 "number": 9, 65 "sum": 243 66 }, 67 "10:07:34": { 68 "brand": "宝来", 69 "factory": "一汽大众", 70 "number": 3, 71 "sum": 123 72 } 73 }, 74 "2016-10-07": { 75 "16:48:35": { 76 "brand": "奔驰", 77 "factory": "一汽大众", 78 "number": 2, 79 "sum": 166 80 } 81 } 82 }
>car.json
1 { 2 "奔驰": { 3 "factory": "一汽大众", 4 "last": 31, 5 "price": 83, 6 "sell": 49, 7 "sum": 80, 8 "other": [123, true, "hello, world", { 9 "梅赛德斯奔驰": "心所向, 持以恒" 10 }] 11 } 12 }
3、cjson介绍
》安装——安装文件:cJSON-master.zip
>mkdir ToolKit (cJSON-master.zipk拷贝至此文件)
>unzip cJSON-master.zip
>cd cJSON-master
>vi README.md
>ls
》使用:直接把cJSON.h和JSON.c拷贝到要使用的目录下即可,然后编译gcc xxx.c cJSON.c -o xxx。
》cjson api:
4、cjson api创建json文件
先把cJSON.h和JSON.c拷贝到要使用的目录下。
》准备创建的文件为:car.json
1 { 2 "奔驰": { 3 "factory": "一汽大众", 4 "last": 31, 5 "price": 83, 6 "sell": 49, 7 "sum": 80, 8 "other": [123, true, "hello, world", { 9 "梅赛德斯奔驰": "心所向, 持以恒" 10 }] 11 } 12 }
》创建如下:
>touch Json_create.c
>vi Json_create.c
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <stdlib.h> 4 #include <sys/types.h> 5 #include <sys/stat.h> 6 #include <string.h> 7 #include "cJSON.h" 8 9 int main(int argc, const char* argv[]) 10 { 11 // 创建对象 12 cJSON* obj = cJSON_CreateObject(); 13 14 // 创建子对象 15 cJSON* subObj = cJSON_CreateObject(); 16 // 添加key-value 17 cJSON_AddItemToObject(subObj, "factory", cJSON_CreateString("一汽大众")); 18 cJSON_AddItemToObject(subObj, "last", cJSON_CreateNumber(31)); 19 cJSON_AddItemToObject(subObj, "price", cJSON_CreateNumber(83)); 20 cJSON_AddItemToObject(subObj, "sell", cJSON_CreateNumber(49)); 21 cJSON_AddItemToObject(subObj, "sum", cJSON_CreateNumber(80)); 22 23 // 创建json数组 24 cJSON* array = cJSON_CreateArray(); 25 // array添加元素 26 cJSON_AddItemToArray(array, cJSON_CreateNumber(124)); 27 cJSON_AddItemToArray(array, cJSON_CreateBool(1)); 28 cJSON_AddItemToArray(array, cJSON_CreateString("hello, world")); 29 30 // 数组中的对象 31 cJSON* subsub = cJSON_CreateObject(); 32 cJSON_AddItemToObject(subsub, "梅赛德斯奔驰", 33 cJSON_CreateString("心所向, 持以恒")); 34 cJSON_AddItemToArray(array, subsub); 35 36 cJSON_AddItemToObject(subObj, "other", array); 37 38 // obj中添加key - value 39 cJSON_AddItemToObject(obj, "奔驰", subObj); 40 41 // 数据格式化 42 char* data = cJSON_Print(obj); 43 FILE* fp = fopen("car.json", "w"); 44 fwrite(data, sizeof(char), strlen(data)+1, fp); 45 fclose(fp); 46 47 return 0; 48 }
>gcc Json_create.c cJSON.c -o create
》报错截图如下:
分析:floor向下取整,使用了数学库math,所以编译时加入-lm
>gcc Json_create.c cJSON.c -o create -lm
>./create
>cat car.json
5、cjson解析api
》cjson解析json文件:
》解析出来后如何把cJSON类型转换为相应的属性?
查看cJSON.h:
》练习—解析car.json:
》文件准备:car.json
>car.json
1 { 2 "奔驰": { 3 "factory": "一汽大众", 4 "last": 31, 5 "price": 83, 6 "sell": 49, 7 "sum": 80, 8 "other": [123, true, "hello, world", { 9 "梅赛德斯奔驰": "心所向, 持以恒" 10 }] 11 } 12 }
>touch parsecar.c
>vi parsecar.c
1 #include <stdio.h> 2 #include <string.h> 3 #include "cJSON.h" 4 5 int main(int argc, const char* argv[]) 6 { 7 if(argc < 2) 8 { 9 printf("./a.out jsonfile\n"); 10 return 0; 11 } 12 13 // 加载json文件 14 FILE* fp = fopen(argv[1], "r"); 15 char buf[1024] = {0}; 16 fread(buf, 1, sizeof(buf), fp); 17 cJSON* root = cJSON_Parse(buf); 18 19 cJSON* subobj = cJSON_GetObjectItem(root, "奔驰"); 20 // 判断对象是否存在 21 if( subobj ) 22 { 23 // 获取子对象 24 cJSON* factory = cJSON_GetObjectItem(subobj, "factory"); 25 cJSON* last = cJSON_GetObjectItem(subobj, "last"); 26 cJSON* price = cJSON_GetObjectItem(subobj, "price"); 27 cJSON* sell = cJSON_GetObjectItem(subobj, "sell"); 28 cJSON* sum = cJSON_GetObjectItem(subobj, "sum"); 29 cJSON* other = cJSON_GetObjectItem(subobj, "other"); 30 31 // 打印value值 另一种方式:cJSON_Print可以把cJSON*强制转为字符串 32 printf("奔驰:\n"); 33 printf(" factory: %s\n", cJSON_Print(factory)); 34 printf(" last: %s\n", cJSON_Print(last)); 35 printf(" price: %s\n", cJSON_Print(price)); 36 printf(" sell: %s\n", cJSON_Print(sell)); 37 printf(" sum: %s\n", cJSON_Print(sum)); 38 39 // 打印数组内容 40 printf(" other:\n"); 41 if(other->type == cJSON_Array) 42 { 43 for(int i=0; i<cJSON_GetArraySize(other); ++i) 44 { 45 cJSON* node = cJSON_GetArrayItem(other, i); 46 // 判断数据类型 47 if(node->type == cJSON_String) 48 { 49 printf(" %s \n", node->valuestring); 50 } 51 if(node->type == cJSON_Number) 52 { 53 printf(" %d\n", node->valueint); 54 } 55 if(node->type == cJSON_True) 56 { 57 printf(" %d\n", node->valueint);//node无bool类型 58 } 59 if(node->type == cJSON_False) 60 { 61 printf(" %d\n", node->valueint); 62 } 63 } 64 } 65 } 66 67 cJSON_Delete(root); 68 fclose(fp); 69 70 71 return 0; 72 }
>gcc parsecar.c cJSON.c -o parse -lm
>./parse car.json
》附:QT中的json类
在学习Linux高并发网络编程开发总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。