1. 程式人生 > >libxml2庫在嵌入式linux中的應用

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

傳送資料。。。

效果圖如下: