现在的位置: 首页 > 综合 > 正文

【通信】XML和TLV打包解包性能比较

2018年04月23日 ⁄ 综合 ⁄ 共 9472字 ⁄ 字号 评论关闭

转载自:http://blog.csdn.net/chexlong/article/details/7016361

在开发协议的时候,经常会碰到打包解包的问题,不同的项目,不同的平台环境下会要求使用不同的打包解包格式,比如PC(Windows,Linux),或嵌入式设备(Arm),本文就使用XML(TinyXml库)和TLV打包解包的性能,做以比较。

    有关TinyXml和TLV的介绍,在之前的博客TinyXml 读写XML文件TLV
格式及编解码示例
中可以看到。这里就不多说了。下边着重介绍一下我做的一个试验,请看Demo程序XML_VS_TLV,其完整源代码可以在此处下载:http://download.csdn.net/detail/chexlong/3845666,在VS2005环境下已经测试通过。

 

  1. // XML_VS_TLV.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #define  IN  
  5. #define  OUT  
  6. #define  RET_OK                 0  
  7. #define  RET_ERR                -1  
  8. #define  RET_PARAM              -2  
  9. #define  RET_MEM                -3  
  10. #define  RET_PACKET             -4  
  11. #define  NAME_LENGTH            20  
  12. #define  COLOR_LENGTH           20  
  13. #define  BUF_SIZE               32  
  14.   
  15. //猫  
  16. typedef struct _CAT_INFO  
  17. {  
  18.     char szName[12]; //名字  
  19.     int iAge; //年龄  
  20.     int iColor; //颜色  
  21. }CAT_INFO,*LPCAT_INFO;  
  22.   
  23.   
  24. /* 
  25. *   XML 格式 
  26. *    
  27. *   选用XML格式编译时,将TLV格式对应宏 #define TLV_FORMAT 注释掉 
  28. *   将宏#define XML_FORMAT启用即可 
  29. */  
  30.   
  31. #define XML_FORMAT  
  32. #if defined (XML_FORMAT)  
  33.   
  34. #include <Windows.h>  
  35. #include "tinyxml.h"  
  36.   
  37. #define  XML_STR_FILE           "cat.xml"  
  38. #define  XML_STR_ROOT           "xml-root"  
  39. #define  XML_STR_CAT_INFO       "cat-info"  
  40. #define  XML_STR_CAT_NUM        "num"  
  41. #define  XML_STR_CAT_AGE        "age"  
  42. #define  XML_STR_CAT_COLOR      "color"  
  43. #define  XML_STR_CAT_NAME       "name"  
  44.   
  45.   
  46. #define  XML_RETURN(x)          { if( RET_OK != (x) ) return RET_ERR; }  
  47. #define  XML_ASSERT(x)          { if( NULL == (x) ) return RET_ERR; }  
  48.   
  49.   
  50. //添加节点  
  51. int AddLeafNode( TiXmlNode* pElmParent, const char* pszNode, const char* pszText )  
  52. {  
  53.     TiXmlElement elmNode(pszNode);  
  54.     TiXmlText elmText(pszText);  
  55.     XML_ASSERT( elmNode.InsertEndChild( elmText ) );  
  56.     XML_ASSERT( pElmParent->InsertEndChild( elmNode ) );  
  57.     return RET_OK;  
  58. }  
  59.   
  60. //删除节点  
  61. int GetLeafNode( TiXmlNode* pElmParent, char* pszNode, char* pszText )  
  62. {     
  63.     TiXmlNode *pTemp;  
  64.     if ( pElmParent && ( pTemp = pElmParent->FirstChild( pszNode ) ) )  
  65.     {  
  66.         if ( pTemp = pTemp->FirstChild() )  
  67.         {  
  68.             strcpy( pszText, pTemp->Value() );  
  69.             return RET_OK;  
  70.         }  
  71.     }  
  72.     return RET_ERR;  
  73. }  
  74.   
  75. //XML格式打包  
  76. int XML_EncodeCat(LPCAT_INFO pCatInfo, char *pBuf,unsigned int &iLen)  
  77. {  
  78.     if (!pCatInfo)  
  79.     {  
  80.         return RET_PARAM;  
  81.     }  
  82.   
  83.     TiXmlDeclaration Declaration( "1.0","""" ); // 建立XML头结构  
  84.     TiXmlDocument xmlDoc;   
  85.     xmlDoc.InsertEndChild( Declaration ); // 把XML头结构插入当前文档  
  86.     TiXmlElement elmRoot(XML_STR_ROOT);  
  87.   
  88.     char szBuf[128];  
  89.     szBuf[0] = '\0';  
  90.   
  91.     //猫的名字  
  92.     if ( '\0' != pCatInfo->szName[0] )  
  93.     {  
  94.         XML_RETURN( AddLeafNode( &elmRoot, XML_STR_CAT_NAME, pCatInfo->szName ) );  
  95.     }  
  96.   
  97.     //年龄  
  98.     sprintf( szBuf,"%d", pCatInfo->iAge );  
  99.     XML_RETURN( AddLeafNode( &elmRoot, XML_STR_CAT_AGE, szBuf ) );  
  100.   
  101.     //颜色  
  102.     sprintf( szBuf,"%d", pCatInfo->iColor );  
  103.     XML_RETURN( AddLeafNode( &elmRoot, XML_STR_CAT_COLOR, szBuf ) );  
  104.   
  105.     XML_ASSERT( xmlDoc.InsertEndChild( elmRoot ) );  
  106.   
  107.     //转换成字符串  
  108.     if ( !xmlDoc.DocToString(pBuf,iLen,iLen) )  
  109.     {  
  110.         return RET_PACKET;  
  111.     }  
  112.   
  113.     return RET_OK;  
  114. }  
  115.   
  116. //XML格式解包  
  117. int XML_DecodeCat(char *pBuf, int iLen, LPCAT_INFO pCatInfo)  
  118. {  
  119.     TiXmlDocument xmlDoc;  
  120.     const char * p = NULL;  
  121.     p = xmlDoc.Parse(pBuf);  
  122.     if ( !p )  
  123.     {  
  124.         return RET_PACKET;  
  125.     }  
  126.     TiXmlNode *pRootNode;  
  127.     char szBuf[128];  
  128.     szBuf[0] = '\0';  
  129.   
  130.     XML_ASSERT( pRootNode = xmlDoc.RootElement() );  
  131.     GetLeafNode(pRootNode,XML_STR_CAT_NAME,pCatInfo->szName);  
  132.   
  133.     GetLeafNode(pRootNode,XML_STR_CAT_AGE,szBuf);  
  134.     pCatInfo->iAge = atoi(szBuf);  
  135.   
  136.     GetLeafNode(pRootNode,XML_STR_CAT_COLOR,szBuf);  
  137.     pCatInfo->iColor = atoi(szBuf);  
  138.   
  139.     return RET_OK;  
  140. }  
  141.   
  142. #endif // #if defined (XML_FORMAT)  
  143.   
  144.   
  145. /* 
  146. *   TLV 格式 
  147. *    
  148. *   选用TLV格式编译时,将XML格式对应宏 #define XML_FORMAT 注释掉 
  149. *   将宏#define TLV_FORMAT启用即可 
  150. */  
  151.   
  152. //#define TLV_FORMAT  
  153. #if defined(TLV_FORMAT)  
  154.   
  155. #include <stdio.h>  
  156. #include <WinSock2.h>  
  157. #include <string>  
  158.   
  159. #pragma comment(lib, "WS2_32")  
  160.   
  161. enum emTLVNodeType  
  162. {  
  163.     emTlvNNone = 0,  
  164.     emTlvNRoot,         //根节点  
  165.     emTlvName,          //名字  
  166.     emTlvAge,           //年龄  
  167.     emTlvColor          //颜色 1 白色 2 黑色  
  168. };  
  169.   
  170. class CTlvPacket  
  171. {  
  172. public:  
  173.     CTlvPacket(char *pBuf,unsigned int len):m_pData(pBuf),m_uiLength(len),m_pEndData(m_pData+len),m_pWritePtr(m_pData),m_pReadPtr(m_pData) { }  
  174.     ~CTlvPacket() { }  
  175.   
  176.     bool WriteInt(int data,bool bMovePtr = true)  
  177.     {  
  178.         int tmp = htonl(data);  
  179.         return Write(&tmp,sizeof(int));  
  180.     }  
  181.   
  182.     bool Write(const void *pDst,unsigned int uiCount)  
  183.     {  
  184.         ::memcpy(m_pWritePtr,pDst,uiCount);  
  185.         m_pWritePtr += uiCount;  
  186.         return m_pWritePtr < m_pEndData ? true : false;  
  187.     }  
  188.   
  189.     bool ReadInt(int *data,bool bMovePtr = true)  
  190.     {  
  191.         Read(data,sizeof(int));  
  192.         *data = ntohl(*data);  
  193.         return true;  
  194.     }  
  195.   
  196.     bool Read(void *pDst,unsigned int uiCount)  
  197.     {  
  198.         ::memcpy(pDst,m_pReadPtr,uiCount);  
  199.         m_pReadPtr += uiCount;  
  200.         return m_pReadPtr < m_pEndData ? true : false;  
  201.     }  
  202.   
  203. private:  
  204.     char *m_pData;  
  205.     unsigned int m_uiLength;  
  206.     char *m_pEndData;  
  207.     char *m_pWritePtr;  
  208.     char *m_pReadPtr;  
  209. };  
  210.   
  211. /* 
  212.  
  213. 格式: 
  214. root L1 V 
  215.         T L V T L V T L V 
  216.  
  217. L1 的长度即为“T L V T L V T L V”的长度 
  218.  
  219. */  
  220.   
  221. //TLV格式打包  
  222. int TLV_EncodeCat(LPCAT_INFO pCatInfo, char *pBuf, unsigned int &iLen)  
  223. {  
  224.     if (!pCatInfo || !pBuf)  
  225.     {  
  226.         return RET_PARAM;  
  227.     }  
  228.   
  229.     CTlvPacket enc(pBuf,iLen);  
  230.     enc.WriteInt(emTlvNRoot);  
  231.     enc.WriteInt(20+12+12); //length   
  232.   
  233.     enc.WriteInt(emTlvName);//猫的名字  
  234.     enc.WriteInt(12);  
  235.     enc.Write(pCatInfo->szName,12);  
  236.   
  237.     enc.WriteInt(emTlvAge);//年龄  
  238.     enc.WriteInt(4);  
  239.     enc.WriteInt(pCatInfo->iAge);  
  240.   
  241.     enc.WriteInt(emTlvColor);//颜色  
  242.     enc.WriteInt(4);  
  243.     enc.WriteInt(pCatInfo->iColor);  
  244.   
  245.     iLen = 8+20+12+12;  
  246.   
  247.     return RET_OK;  
  248. }  
  249.   
  250. //TLV格式解包  
  251. int TLV_DecodeCat(char *pBuf, int iLen, LPCAT_INFO pCatInfo)  
  252. {  
  253.     if (!pCatInfo || !pBuf)  
  254.     {  
  255.         return RET_PARAM;  
  256.     }  
  257.   
  258.     CTlvPacket encDec(pBuf,iLen);  
  259.     int iType;  
  260.     int iSum,iLength;  
  261.   
  262.     encDec.ReadInt(&iType);  
  263.     if (emTlvNRoot != iType)  
  264.     {  
  265.         return RET_PACKET;  
  266.     }  
  267.     encDec.ReadInt(&iSum);  
  268.   
  269.     while (iSum > 0)  
  270.     {  
  271.         encDec.ReadInt(&iType);  
  272.         encDec.ReadInt(&iLength);  
  273.         switch(iType)  
  274.         {  
  275.         case emTlvName:  
  276.             encDec.Read(pCatInfo->szName,12);  
  277.             iSum -= 20;  
  278.             break;  
  279.         case emTlvAge:  
  280.             encDec.ReadInt(&pCatInfo->iAge);  
  281.             iSum -= 12;  
  282.             break;  
  283.         case emTlvColor:  
  284.             encDec.ReadInt(&pCatInfo->iColor);  
  285.             iSum -= 12;  
  286.             break;  
  287.         default:  
  288.             printf("TLV_DecodeCat unkonwn error. \n");  
  289.             break;  
  290.         }  
  291.     }  
  292.   
  293.     return RET_OK;  
  294. }  
  295. #endif //#if defined(TLV_FORMAT)  
  296.   
  297.   
  298.   
  299. int main(int argc, char* argv[])  
  300. {  
  301.     int iRet;  
  302.     unsigned int iLen;  
  303.     char buf[256] = {0};  
  304.   
  305.     CAT_INFO cat;  
  306.     memset(&cat,0,sizeof(cat));  
  307.     strcpy(cat.szName,"Tom");  
  308.     cat.iAge = 5;  
  309.     cat.iColor = 2;  
  310.     DWORD dwStart,dwEnd;  
  311.     dwStart = ::GetTickCount();  
  312.   
  313.     //循环一万次,可以明显看出用时区别  
  314.     for (int i = 0; i < 10000; i++)  
  315.     {  
  316.         memset(buf,0,sizeof(buf));  
  317. #if defined XML_FORMAT  
  318.         iRet = XML_EncodeCat(&cat,buf,iLen);  
  319.         if ( RET_OK == iRet )  
  320.         {  
  321.             //printf("XML_EncodeCat ok, iLen = %d. \n",iLen);  
  322.         }  
  323.         else  
  324.         {  
  325.             printf("XML_EncodeCat error \n");  
  326.         }  
  327.   
  328.         memset(&cat,0,sizeof(cat));  
  329.         iRet = XML_DecodeCat(buf,iLen,&cat);  
  330.         if ( RET_OK == iRet )  
  331.         {  
  332.             //printf("XML_DecodeCat ok, No = %d, cat name = %s, age = %d, color = %d. \n",i, cat.szName,cat.iAge,cat.iColor);  
  333.         }  
  334.         else  
  335.         {  
  336.             printf("XML_DecodeCat error, code = %d. \n", iRet);  
  337.         }  
  338. #elif defined TLV_FORMAT  
  339.         iRet = TLV_EncodeCat(&cat,buf,iLen);  
  340.         if ( RET_OK == iRet )  
  341.         {  
  342.             //printf("TLV_EncodeCat ok, iLen = %d. \n",iLen);  
  343.         }  
  344.         else  
  345.         {  
  346.             printf("TLV_EncodeCat error \n");  
  347.         }  
  348.   
  349.         memset(&cat,0,sizeof(cat));  
  350.         iRet = TLV_DecodeCat(buf,iLen,&cat);  
  351.         if ( RET_OK == iRet )  
  352.         {  
  353.             //printf("TLV_DecodeCat ok, No = %d, cat name = %s, age = %d, color = %d. \n",i, cat.szName,cat.iAge,cat.iColor);  
  354.         }  
  355.         else  
  356.         {  
  357.             printf("TLV_DecodeCat error, code = %d. \n", iRet);  
  358.         }  
  359. #endif  
  360.     }  
  361.   
  362.     dwEnd = ::GetTickCount();  
  363.   
  364.     DWORD dwMilliseconds = dwEnd - dwStart;  
  365. #if defined XML_FORMAT  
  366.     printf("XML dwMilliseconds = %ld",dwMilliseconds);  
  367. #elif defined TLV_FORMAT  
  368.     printf("TLV dwMilliseconds = %ld",dwMilliseconds);  
  369. #endif  
  370.     int iWait = getchar();  
  371.     return 0;  
  372. }  


 

    分别用XML格式和TLV格式对代码进行编译。下边是Debug模式下使用XML和TLV内存截图:

XML格式

 

TLV格式

    从上图可以看出,XML格式具有很好的可读性,容易阅读也就容易书写,这是XML的优点。而使用TLV格式,却不容易阅读,看起来很费劲的样子,但是它是二进制的,接近计算机的语言,所以对计算机来说很容易阅读。

    在同一台电脑上(我本本是XP系统,不同的电脑上运行会有差异),手动点击XML_VS_TLV.exe,以观察在XML和TLV格式下运行程序的耗时情况

 

次数 XML /毫秒 TLV /毫秒
1 2297 31
2 2265 31
3 2281 31
4 2266 31
5 2265 31
平均 2274.8 31.2

 

    从上表可以看出,使用XML格式打包解包,在我的PC上平均耗时2274.8毫秒,而使用TLV则耗时31.2毫秒,使用TLV大约可以节约72.9倍的效率。

    总结:使用XML,其打包包体可读性非常好,容易书写,但性能不及TLV;使用TLV,其性能远要好于XML,但可读性较差。在对性能要求不是很高的情况下,比如PC上的协议开发,宜使用XML格式,这样非常方便进行调试程序;在对性能要求很高的情况下,比如嵌入式设备Arm平台,宜使用TLV格式,这样非常节约CPU资源,也节省内存。

抱歉!评论已关闭.