接上回,
上次说到协议使用XML格式的,就是为了能够比较好的通过已有的函数库来解析,那在JAVA里常用的就是DOM4J,在C里笔者这次用的是libxml2,官方地址是:http://xmlsoft.org/ 可以下载和查看API文档。
libxml2要先安装了才能使用,笔者是直接用yum安装的,如果想手动装的话就可以看看这篇博文:
http://blog.youkuaiyun.com/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 xmlNode
struct _xmlNode {
void * _private : application data
xmlElementType type : 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 short line : line number
unsigned short extra : extra data for XPath/XSLT
}
这里面有children 指针和next 指针,笔者主要就是用到这两个指针。通过children 来找到第一个子节点,然后再根据next 来逐个查找,找到了就返回这个指针,这就是这个findNodeByName 的过程了。
找到需要的节点后使用xmlNodeGetContent 来获取节点下的内容,笔者这里就根据之前订好的协议来解析响应了。
首先是通过information 节点下的code 节点来看响应类型,再根据响应的不同来给出不同的输出:
// 如果找到了code节点
if(strcmp(xmlNodeGetContent(code),"200")==0){
// 如果code节点内容为200
messages = findNodeByName(root, "messages");
outputMessages(messages);
returnCode = 200;
}else if(strcmp(xmlNodeGetContent(code), "201")==0) {
// 如果code节点内容为201
room = 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;
}
--------------------------------------
这次就先到这吧...