libxml2庫在嵌入式linux中的應用
在實際的專案開發過程中,往往有這麼一個通用的架構:主應用程式,配置檔案,和配置檔案控制程式。主應用程式往往是根據配置檔案內容來執行的,而控制程式是為給人機互動的。
我也是最近想了解一下這種架構方式,於是在網上找了一些關於配置檔案相關的資訊,最終確認使用xml檔案,因為它比較簡單、通用、而功能強大。linux+c最終選擇了libxml2庫。
關於環境搭建十分簡單,參考我的上一篇部落格:http://blog.csdn.net/yuanbinquan/article/details/43485323
下面是我使用xml庫做的一個簡單的demo,demo所針對的問題:在實際的產品中,網路通訊(socket)資料是不會打印出來了的,因為完全沒這個必要,且浪費系統資源。但是現場維護人員在分析裝置問題時,可能需要檢視通訊層的資料,這樣就可以通過應用程式的配置檔案引數來決定是否列印通訊資料。demo十分粗糙,僅僅實現了該功能,如有不對之處,請各位指正!大力批評
應用程式:兩個通訊的socket程式
service.c:
/*
gcc -o server server.c -I /home/libxml2-2.9.2/Demo/include/libxml2/ -L /home/libxml2-2.9.2/Demo/lib/ -lxml2
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#define XML_FILE_NAME "net.xml"
#define DEFAULT_PORT 8000
#define MAXLINE 4096
static int CreateDefaultXml()
{
xmlDocPtr doc;
xmlNodePtr root_node, NetNode, grandson, GuiNode;
int nRel;
doc = xmlNewDoc(BAD_CAST"1.0");
root_node = xmlNewNode(NULL,BAD_CAST"net.xml");
//設定根節點
xmlDocSetRootElement(doc,root_node);
/* 網路模組配置 */
NetNode = xmlNewNode(NULL, BAD_CAST "net");
xmlAddChild(root_node,NetNode);
grandson = xmlNewNode(NULL, BAD_CAST "print");
xmlAddChild(NetNode,grandson);
xmlAddChild(grandson, xmlNewText(BAD_CAST "1"));
/*GUI 模組配置 */
GuiNode = xmlNewNode(NULL, BAD_CAST "gui");
xmlAddChild(root_node,GuiNode);
grandson = xmlNewNode(NULL, BAD_CAST "type");
xmlAddChild(GuiNode,grandson);
xmlAddChild(grandson, xmlNewText(BAD_CAST "0x01"));
//儲存xml文件
nRel = xmlSaveFile(XML_FILE_NAME,doc);
if (nRel != -1)
{
printf("Create xml file,write %d byte! \n", nRel);
}
if(nRel == -1)
{
printf("Create xml file failed!\n");
return -1;
}
//釋放文件內節點動態申請的記憶體
xmlFreeDoc(doc);
xmlCleanupParser();
xmlMemoryDump();
return 0;
}
static int xmlParse(char * Mode, char *ctl, char *value)
{
xmlDocPtr doc; //定義解析文件指標
xmlNodePtr curNode; //定義結點指標(你需要它為了在各個結點間移動)
xmlChar *szKey; //臨時字串變數
doc = xmlReadFile(XML_FILE_NAME,"GB2312",XML_PARSE_RECOVER); //解析檔案
//檢查解析文件是否成功,如果不成功,libxml將指一個註冊的錯誤並停止。
//一個常見錯誤是不適當的編碼。XML標準文件除了用UTF-8或UTF-16外還可用其它編碼儲存。
//如果文件是這樣,libxml將自動地為你轉換到UTF-8。更多關於XML編碼資訊包含在XML標準中.
if (NULL == doc)
{
fprintf(stderr,"Document not parsed successfully\n");
return -1;
}
curNode = xmlDocGetRootElement(doc); //確定文件根元素
/*檢查確認當前文件中包含內容*/
if (NULL == curNode)
{
fprintf(stderr,"empty document\n");
xmlFreeDoc(doc);
return -1;
}
/*在這個例子中,我們需要確認文件是正確的型別。"root"是在這個示例中使用文件的根型別。*/
if (xmlStrcmp(curNode->name, BAD_CAST "net.xml"))
{
fprintf(stderr,"document of the wrong type, root node != net.xml");
xmlFreeDoc(doc);
return -1;
}
curNode = curNode->xmlChildrenNode;
while(curNode != NULL)
{
//取出節點中的內容
if ((!xmlStrcmp(curNode->name, (const xmlChar *)"net")))
{
curNode = curNode->xmlChildrenNode;
while(curNode != NULL)
{
//取出節點中的內容
if ((!xmlStrcmp(curNode->name, (const xmlChar *)"print")))
{
szKey = xmlNodeGetContent(curNode);
//printf("net.xml: %s\n", szKey);
memcpy(value, szKey, strlen(szKey));
xmlFree(szKey);
return 0;
}
curNode = curNode->next;
}
}
curNode = curNode->next;
}
xmlFreeDoc(doc);
return -1;
}
int main(int argc, char** argv)
{
int socket_fd, connect_fd;
struct sockaddr_in servaddr;
char xmlRet[512]={0};
char buff[4096];
int n;
if(access(XML_FILE_NAME, F_OK))
{
printf("Create xml file\n");
if(CreateDefaultXml())
{
return -1;
}
}
//初始化Socket
if( (socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){
printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
//初始化
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址設定成INADDR_ANY,讓系統自動獲取本機的IP地址。
servaddr.sin_port = htons(DEFAULT_PORT);//設定的埠為DEFAULT_PORT
//將本地地址繫結到所建立的套接字上
if( bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
//開始監聽是否有客戶端連線
if( listen(socket_fd, 10) == -1){
printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
printf("======waiting for client's request======\n");
while(1){
//阻塞直到有客戶端連線,不然多浪費CPU資源。
if( (connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1){
printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
continue;
}
//接受客戶端傳過來的資料
n = recv(connect_fd, buff, MAXLINE, 0);
//向客戶端傳送迴應資料
if(!fork()){ /*紫禁城*/
if(send(connect_fd, "Hello,you are connected!\n", 26,0) == -1)
perror("send error");
close(connect_fd);
exit(0);
}
memset(xmlRet, 0, sizeof(xmlRet));
if(!(xmlParse("net", "print", xmlRet)))
{
if(xmlRet[0] == '1')
{
int iTmp;
printf("recv data len :%d\n",n);
for(iTmp = 0; iTmp < n; iTmp++)
{
if(iTmp % 0x10 == 0)
{
printf("\n");
}
if(iTmp % 0x04 == 0)
{
printf("\t");
}
printf("%x ", buff[iTmp]);
}
printf("\n");
}
}
/*
自己的訊息處理函式
*/
close(connect_fd);
}
close(socket_fd);
}
client.c:
/*
gcc -o client client.c
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define MAXLINE 4096
int main(int argc, char** argv)
{
int sockfd, n,rec_len;
char recvline[4096], sendline[4096];
char buf[MAXLINE];
struct sockaddr_in servaddr;
if( argc != 2){
printf("usage: ./client <ipaddress>\n");
exit(0);
}
if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
exit(0);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(8000);
if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){
printf("inet_pton error for %s\n",argv[1]);
exit(0);
}
if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
{
printf("connect error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
printf("send msg to server: \n");
fgets(sendline, 4096, stdin);
if( send(sockfd, sendline, strlen(sendline), 0) < 0)
{
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
if((rec_len = recv(sockfd, buf, MAXLINE,0)) == -1) {
perror("recv error");
exit(1);
}
buf[rec_len] = '\0';
printf("Received : %s ",buf);
close(sockfd);
exit(0);
}
配置檔案控制程式:
netctl.c:
/*
gcc -o guictl guictl.c -I /home/libxml2-2.9.2/Demo/include/libxml2/ -L /home/libxml2-2.9.2/Demo/lib/ -lxml2
*/
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#define XML_FILE_NAME "net.xml"
enum{
NET = 1,
GUI,
}Mode_VALUE;
enum{
PRINT = 1,
}NET_VALUE;
static int Usage(char *argv0)
{
if(argv0 == NULL)
{
return -1;
}
printf("%s mode ctl value\n", argv0);
return 0;
}
/*
return -1:error
0 :不支援的命令
1:net
2:gui
*/
static int ModeCheck(char *mode)
{
if(mode == NULL)
{
return -1;
}
if(!memcmp(mode, "net", strlen("net")))
{
return NET;
}
else if(!memcmp(mode, "gui", strlen("gui")))
{
return GUI;
}
return 0;
}
/*
return: -1:error
0 :不支援的命令
1:print
*/
static int NetModeCtlCheck(char *ctl)
{
if(ctl == NULL)
{
return -1;
}
if(!memcmp(ctl, "print", strlen("print")))
{
return PRINT;
}
return 0;
}
/*
return -1:error
0 :不支援的命令
1:success
*/
static int NetModeProc(char *ctl, char *value)
{
int iRet;
xmlDocPtr doc; //定義解析文件指標
xmlNodePtr curNode; //定義結點指標(你需要它為了在各個結點間移動)
xmlChar *szKey;
if((ctl == NULL) || (value == NULL))
{
return -1;
}
iRet = NetModeCtlCheck(ctl);
if(iRet < 0)
{
return -1;
}
else if( iRet == 0)
{
return 0;
}
switch(iRet)
{
case PRINT:
switch(*value)
{
case '1':
case '0':
doc = xmlReadFile(XML_FILE_NAME,"GB2312",XML_PARSE_RECOVER);
if (NULL == doc)
{
fprintf(stderr,"Document not parsed successfully.\n");
return -1;
}
curNode = xmlDocGetRootElement(doc); //確定文件根元素
/*檢查確認當前文件中包含內容*/
if (NULL == curNode)
{
fprintf(stderr,"empty document\n");
xmlFreeDoc(doc);
return -1;
}
if (xmlStrcmp(curNode->name, BAD_CAST "net.xml"))
{
fprintf(stderr,"document of the wrong type, root node != root");
xmlFreeDoc(doc);
return -1;
}
curNode = curNode->xmlChildrenNode;
while(curNode != NULL)
{
//取出節點中的內容
if ((!xmlStrcmp(curNode->name, (const xmlChar *)"net")))
{
curNode = curNode->xmlChildrenNode;
while(curNode != NULL)
{
//取出節點中的內容
if ((!xmlStrcmp(curNode->name, (const xmlChar *)"print")))
{
szKey = xmlNodeGetContent(curNode);
xmlNodeSetContent(curNode, BAD_CAST value);
xmlFree(szKey);
iRet = xmlSaveFile(XML_FILE_NAME,doc);
if (iRet != -1)
{
printf("Create xml file,write %d byte! \n", iRet);
}
if(iRet == -1)
{
printf("Create xml file failed!\n");
return -1;
}
return 1;
}
curNode = curNode->next;
}
}
curNode = curNode->next;
}
break;
default:
return 0;
}
break;
}
return 0;
}
int main(int argc, char *argv[])
{
int iTmp, iRet;
for(iTmp = 0; iTmp < argc; iTmp++)
{
printf("%s\n", argv[iTmp]);
}
if(memcmp(argv[0], "./netctl", strlen( "./netctl")))
{
printf("File %s Function %s Line %d >\n", __FILE__, __FUNCTION__, __LINE__);
goto usage;
}
/*mode net/gui */
if((iRet = ModeCheck(argv[1]))<= 0)
{
printf("File %s Function %s Line %d >\n", __FILE__, __FUNCTION__, __LINE__);
goto usage;
}
switch(iRet)
{
case NET:
iRet = NetModeProc(argv[2], argv[3]);
if(iRet < 0)
{
printf("NetModeProc err!\n");
}
else if(iRet == 0)
{
printf("File %s Function %s Line %d >\n", __FILE__, __FUNCTION__, __LINE__);
goto usage;
}
break;
case GUI:
;//
break;
}
return 0;
usage:
Usage(argv[0]);
return -1;
}
然後執行測試程式:
終端一:
# ./server
執行服務程式後,會在當前目錄下生成net.xml檔案。
終端二:
#./client
傳送資料。。。
#./netctl print 0
0關閉列印,1開啟列印
#./client
傳送資料。。。
效果圖如下: