接上回,
上次说到协议使用XML格式的,就是为了能够比较好的通过已有的函数库来解析,那在JAVA里常用的就是DOM4J,在C里笔者这次用的是libxml2,官方地址是:http://xmlsoft.org/ 可以下载和查看API文档。
libxml2要先安装了才能使用,笔者是直接用yum安装的,如果想手动装的话就可以看看这篇博文:
http://blog.csdn.net/shanzhizi/article/details/7726679
这次大作业是用VirtualBox虚拟了一个CentOS 6.5出来完成的,如果和笔者用的系统一样呢,那就可以参考一下这个使用方法,当使用了libxml2的函数后,在用GCC编译的时候要这样写:
gcc -I/usr/include/libxml2/ -lxml2 xxx.c
否则由于动态库和头文件找不到的原因会导致无法编译通过。
接下来就以通知的解析过程(int sendMessage)为例来具体说一下libxml2的简单使用:
XML文档解析的基本模型都是类似的,先是Document ,Document 里先找到Root Element 找到Root Element 后再依次解析下面的各个Node 。所以先要有一个Document 的指针,这个struct 叫xmlDocPtr ,然后需要一些Node 的指针, 一般而言两个就可以了,一个是root记录根节点, 另一个是curr记录当前节点。为了方便,笔者用了比较多。
// xml文档指针xmlDocPtr pdoc;// xml 节点指针xmlNodePtr root, room, information, code, messages, detail;
先要发送请求字符串,然后从服务器获取字符串,这个过程之后再写,现在先写获得之后做的事,获得之后是一个字符串,放在receiveStr里面,然后就可以用函数xmlParseMemory 来解析出Document 了,
pdoc = xmlParseMemory(receiveStr, strlen(receiveStr));
然后就可以从Document 里找出Root Element,使用函数是xmlDocGetRootElement
root = xmlDocGetRootElement(pdoc);
然后就去解析下面的节点(注意每一步都应该判断一下是否为空,因为可能因为数据问题导致解析失败)
information = findNodeByName(root, "information");
这个函数是自己写的,貌似在libxml2里没有提供类似这样直接查找的函数,只能一个一个节点看,比较麻烦,所以就抽了出来放在一个函数里面了。
xmlNodePtr findNodeByName(xmlNodePtr parent, char * nodeName){xmlNodePtr temp;if(parent==NULL){printf("empty parent node \n");return NULL;}for(temp = parent->children; temp!=NULL; temp = temp->next){//printf("searching name is ---> %s\n",temp->name);if(strcmp(temp->name, nodeName)==0)return temp;}return NULL;}
这个函数只适用于这次这种简单的情况,因为这次一个Element下所有的节点都不会有重名的情况出现,所以就比较查找出第一个就是那唯一的一个了,不需要考虑多个的情况。
先要看看xmlNodePtr是什么,
Typedef xmlNode * xmlNodePtr
这个就是一个xmlNode的指针,然后再看一下xmlNode
Structure xmlNodestruct _xmlNode { void *_private: application data xmlElementTypetype: type number, must be second ! const xmlChar *name: the name of the node, or the entity struct _xmlNode *children: parent->childs link struct _xmlNode *last: last child link struct _xmlNode *parent: child->parent link struct _xmlNode *next: next sibling link struct _xmlNode *prev: previous sibling link struct _xmlDoc *doc: the containing document End of common p xmlNs *ns: pointer to the associated namespace xmlChar *content: the content struct _xmlAttr *properties: properties list xmlNs *nsDef: namespace definitions on this node void *psvi: for type/PSVI informations unsigned shortline: line number unsigned shortextra: extra data for XPath/XSLT}
这里面有children 指针和next 指针,笔者主要就是用到这两个指针。通过children 来找到第一个子节点,然后再根据next 来逐个查找,找到了就返回这个指针,这就是这个findNodeByName 的过程了。
找到需要的节点后使用xmlNodeGetContent 来获取节点下的内容,笔者这里就根据之前订好的协议来解析响应了。
首先是通过information 节点下的code 节点来看响应类型,再根据响应的不同来给出不同的输出:
// 如果找到了code节点if(strcmp(xmlNodeGetContent(code),"200")==0){// 如果code节点内容为200messages = findNodeByName(root, "messages");outputMessages(messages);returnCode = 200;}else if(strcmp(xmlNodeGetContent(code), "201")==0){// 如果code节点内容为201room = findNodeByName(root, "roomname");detail = findNodeByName(information, "detail");messages = findNodeByName(root, "messages");printf("------------------\n");printf("%s\n\n", room!=NULL?xmlNodeGetContent(room):"unknown room name");outputDetail(detail);printf("------------------\n\n");outputMessages(messages);returnCode = 201;}else if(strcmp(xmlNodeGetContent(code), "500")==0){// 如果code节点内容为500, 服务器异常printf("server error, please try again later\n");returnCode = 500;}else if(strcmp(xmlNodeGetContent(code), "400")==0){// 如果code节点内容为400, 用户已失效printf("time out, please login once more\n");returnCode = 400;}else if(strcmp(xmlNodeGetContent(code), "404")==0){// 如果code节点内容为404, 用户不在房间中printf("you are out of chat room, please find a new one\n");returnCode = 404;}else{printf("can't find conventional status code\n");returnCode = -1;}xmlFreeDoc(pdoc);return returnCode;
最后有个xmlFreeDoc 这个是释放资源的函数,最后记得要调用。另外在前面如果发现某个节点为空需要直接return 的时候也要调用这个函数进行释放。
其中还调用了两个函数,就是专门拿来输出的,下面直接附上其中一个吧,其实就是根据协议来一条条解析,没什么特别的,另一个是类似的
int outputMessages(xmlNodePtr messages){xmlNodePtr message;xmlNodePtr time, name, content;if(messages == NULL){printf("empty messages node\n");return -1;}for(message = messages->children; message != NULL; message = message->next){time = findNodeByName(message, "time");name = findNodeByName(message, "name");content = findNodeByName(message, "content");printf("%s", name!=NULL?xmlNodeGetContent(name):"unknown talker");printf("(");printf("%s", time!=NULL?xmlNodeGetContent(time):"unknown time");printf("):\n");printf("%s\n\n", content!=NULL?xmlNodeGetContent(content):"");}strcpy(_time, xmlNodeGetContent(time));return 0;}
————————————–
这次就先到这吧…
不要害怕错过什么,因为在路上你就已经收获了自由自在的好心情。