转载自: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环境下已经测试通过。
- // XML_VS_TLV.cpp : 定义控制台应用程序的入口点。
- //
- #define IN
- #define OUT
- #define RET_OK 0
- #define RET_ERR -1
- #define RET_PARAM -2
- #define RET_MEM -3
- #define RET_PACKET -4
- #define NAME_LENGTH 20
- #define COLOR_LENGTH 20
- #define BUF_SIZE 32
- //猫
- typedef struct _CAT_INFO
- {
- char szName[12]; //名字
- int iAge; //年龄
- int iColor; //颜色
- }CAT_INFO,*LPCAT_INFO;
- /*
- * XML 格式
- *
- * 选用XML格式编译时,将TLV格式对应宏 #define TLV_FORMAT 注释掉
- * 将宏#define XML_FORMAT启用即可
- */
- #define XML_FORMAT
- #if defined (XML_FORMAT)
- #include <Windows.h>
- #include "tinyxml.h"
- #define XML_STR_FILE "cat.xml"
- #define XML_STR_ROOT "xml-root"
- #define XML_STR_CAT_INFO "cat-info"
- #define XML_STR_CAT_NUM "num"
- #define XML_STR_CAT_AGE "age"
- #define XML_STR_CAT_COLOR "color"
- #define XML_STR_CAT_NAME "name"
- #define XML_RETURN(x) { if( RET_OK != (x) ) return RET_ERR; }
- #define XML_ASSERT(x) { if( NULL == (x) ) return RET_ERR; }
- //添加节点
- int AddLeafNode( TiXmlNode* pElmParent, const char* pszNode, const char* pszText )
- {
- TiXmlElement elmNode(pszNode);
- TiXmlText elmText(pszText);
- XML_ASSERT( elmNode.InsertEndChild( elmText ) );
- XML_ASSERT( pElmParent->InsertEndChild( elmNode ) );
- return RET_OK;
- }
- //删除节点
- int GetLeafNode( TiXmlNode* pElmParent, char* pszNode, char* pszText )
- {
- TiXmlNode *pTemp;
- if ( pElmParent && ( pTemp = pElmParent->FirstChild( pszNode ) ) )
- {
- if ( pTemp = pTemp->FirstChild() )
- {
- strcpy( pszText, pTemp->Value() );
- return RET_OK;
- }
- }
- return RET_ERR;
- }
- //XML格式打包
- int XML_EncodeCat(LPCAT_INFO pCatInfo, char *pBuf,unsigned int &iLen)
- {
- if (!pCatInfo)
- {
- return RET_PARAM;
- }
- TiXmlDeclaration Declaration( "1.0","", "" ); // 建立XML头结构
- TiXmlDocument xmlDoc;
- xmlDoc.InsertEndChild( Declaration ); // 把XML头结构插入当前文档
- TiXmlElement elmRoot(XML_STR_ROOT);
- char szBuf[128];
- szBuf[0] = '\0';
- //猫的名字
- if ( '\0' != pCatInfo->szName[0] )
- {
- XML_RETURN( AddLeafNode( &elmRoot, XML_STR_CAT_NAME, pCatInfo->szName ) );
- }
- //年龄
- sprintf( szBuf,"%d", pCatInfo->iAge );
- XML_RETURN( AddLeafNode( &elmRoot, XML_STR_CAT_AGE, szBuf ) );
- //颜色
- sprintf( szBuf,"%d", pCatInfo->iColor );
- XML_RETURN( AddLeafNode( &elmRoot, XML_STR_CAT_COLOR, szBuf ) );
- XML_ASSERT( xmlDoc.InsertEndChild( elmRoot ) );
- //转换成字符串
- if ( !xmlDoc.DocToString(pBuf,iLen,iLen) )
- {
- return RET_PACKET;
- }
- return RET_OK;
- }
- //XML格式解包
- int XML_DecodeCat(char *pBuf, int iLen, LPCAT_INFO pCatInfo)
- {
- TiXmlDocument xmlDoc;
- const char * p = NULL;
- p = xmlDoc.Parse(pBuf);
- if ( !p )
- {
- return RET_PACKET;
- }
- TiXmlNode *pRootNode;
- char szBuf[128];
- szBuf[0] = '\0';
- XML_ASSERT( pRootNode = xmlDoc.RootElement() );
- GetLeafNode(pRootNode,XML_STR_CAT_NAME,pCatInfo->szName);
- GetLeafNode(pRootNode,XML_STR_CAT_AGE,szBuf);
- pCatInfo->iAge = atoi(szBuf);
- GetLeafNode(pRootNode,XML_STR_CAT_COLOR,szBuf);
- pCatInfo->iColor = atoi(szBuf);
- return RET_OK;
- }
- #endif // #if defined (XML_FORMAT)
- /*
- * TLV 格式
- *
- * 选用TLV格式编译时,将XML格式对应宏 #define XML_FORMAT 注释掉
- * 将宏#define TLV_FORMAT启用即可
- */
- //#define TLV_FORMAT
- #if defined(TLV_FORMAT)
- #include <stdio.h>
- #include <WinSock2.h>
- #include <string>
- #pragma comment(lib, "WS2_32")
- enum emTLVNodeType
- {
- emTlvNNone = 0,
- emTlvNRoot, //根节点
- emTlvName, //名字
- emTlvAge, //年龄
- emTlvColor //颜色 1 白色 2 黑色
- };
- class CTlvPacket
- {
- public:
- 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) { }
- ~CTlvPacket() { }
- bool WriteInt(int data,bool bMovePtr = true)
- {
- int tmp = htonl(data);
- return Write(&tmp,sizeof(int));
- }
- bool Write(const void *pDst,unsigned int uiCount)
- {
- ::memcpy(m_pWritePtr,pDst,uiCount);
- m_pWritePtr += uiCount;
- return m_pWritePtr < m_pEndData ? true : false;
- }
- bool ReadInt(int *data,bool bMovePtr = true)
- {
- Read(data,sizeof(int));
- *data = ntohl(*data);
- return true;
- }
- bool Read(void *pDst,unsigned int uiCount)
- {
- ::memcpy(pDst,m_pReadPtr,uiCount);
- m_pReadPtr += uiCount;
- return m_pReadPtr < m_pEndData ? true : false;
- }
- private:
- char *m_pData;
- unsigned int m_uiLength;
- char *m_pEndData;
- char *m_pWritePtr;
- char *m_pReadPtr;
- };
- /*
- 格式:
- root L1 V
- T L V T L V T L V
- L1 的长度即为“T L V T L V T L V”的长度
- */
- //TLV格式打包
- int TLV_EncodeCat(LPCAT_INFO pCatInfo, char *pBuf, unsigned int &iLen)
- {
- if (!pCatInfo || !pBuf)
- {
- return RET_PARAM;
- }
- CTlvPacket enc(pBuf,iLen);
- enc.WriteInt(emTlvNRoot);
- enc.WriteInt(20+12+12); //length
- enc.WriteInt(emTlvName);//猫的名字
- enc.WriteInt(12);
- enc.Write(pCatInfo->szName,12);
- enc.WriteInt(emTlvAge);//年龄
- enc.WriteInt(4);
- enc.WriteInt(pCatInfo->iAge);
- enc.WriteInt(emTlvColor);//颜色
- enc.WriteInt(4);
- enc.WriteInt(pCatInfo->iColor);
- iLen = 8+20+12+12;
- return RET_OK;
- }
- //TLV格式解包
- int TLV_DecodeCat(char *pBuf, int iLen, LPCAT_INFO pCatInfo)
- {
- if (!pCatInfo || !pBuf)
- {
- return RET_PARAM;
- }
- CTlvPacket encDec(pBuf,iLen);
- int iType;
- int iSum,iLength;
- encDec.ReadInt(&iType);
- if (emTlvNRoot != iType)
- {
- return RET_PACKET;
- }
- encDec.ReadInt(&iSum);
- while (iSum > 0)
- {
- encDec.ReadInt(&iType);
- encDec.ReadInt(&iLength);
- switch(iType)
- {
- case emTlvName:
- encDec.Read(pCatInfo->szName,12);
- iSum -= 20;
- break;
- case emTlvAge:
- encDec.ReadInt(&pCatInfo->iAge);
- iSum -= 12;
- break;
- case emTlvColor:
- encDec.ReadInt(&pCatInfo->iColor);
- iSum -= 12;
- break;
- default:
- printf("TLV_DecodeCat unkonwn error. \n");
- break;
- }
- }
- return RET_OK;
- }
- #endif //#if defined(TLV_FORMAT)
- int main(int argc, char* argv[])
- {
- int iRet;
- unsigned int iLen;
- char buf[256] = {0};
- CAT_INFO cat;
- memset(&cat,0,sizeof(cat));
- strcpy(cat.szName,"Tom");
- cat.iAge = 5;
- cat.iColor = 2;
- DWORD dwStart,dwEnd;
- dwStart = ::GetTickCount();
- //循环一万次,可以明显看出用时区别
- for (int i = 0; i < 10000; i++)
- {
- memset(buf,0,sizeof(buf));
- #if defined XML_FORMAT
- iRet = XML_EncodeCat(&cat,buf,iLen);
- if ( RET_OK == iRet )
- {
- //printf("XML_EncodeCat ok, iLen = %d. \n",iLen);
- }
- else
- {
- printf("XML_EncodeCat error \n");
- }
- memset(&cat,0,sizeof(cat));
- iRet = XML_DecodeCat(buf,iLen,&cat);
- if ( RET_OK == iRet )
- {
- //printf("XML_DecodeCat ok, No = %d, cat name = %s, age = %d, color = %d. \n",i, cat.szName,cat.iAge,cat.iColor);
- }
- else
- {
- printf("XML_DecodeCat error, code = %d. \n", iRet);
- }
- #elif defined TLV_FORMAT
- iRet = TLV_EncodeCat(&cat,buf,iLen);
- if ( RET_OK == iRet )
- {
- //printf("TLV_EncodeCat ok, iLen = %d. \n",iLen);
- }
- else
- {
- printf("TLV_EncodeCat error \n");
- }
- memset(&cat,0,sizeof(cat));
- iRet = TLV_DecodeCat(buf,iLen,&cat);
- if ( RET_OK == iRet )
- {
- //printf("TLV_DecodeCat ok, No = %d, cat name = %s, age = %d, color = %d. \n",i, cat.szName,cat.iAge,cat.iColor);
- }
- else
- {
- printf("TLV_DecodeCat error, code = %d. \n", iRet);
- }
- #endif
- }
- dwEnd = ::GetTickCount();
- DWORD dwMilliseconds = dwEnd - dwStart;
- #if defined XML_FORMAT
- printf("XML dwMilliseconds = %ld",dwMilliseconds);
- #elif defined TLV_FORMAT
- printf("TLV dwMilliseconds = %ld",dwMilliseconds);
- #endif
- int iWait = getchar();
- return 0;
- }
分别用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资源,也节省内存。