基于Linux C的聊天室客户端(二)libxml2的使用

接上回,

上次说到协议使用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 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;
}


--------------------------------------


这次就先到这吧...




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值