在开发协议的时候,经常会碰到打包解包的问题,不同的项目,不同的平台环境下会要求使用不同的打包解包格式,比如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资源,也节省内存。
 
                   
                   
                   
                   
                            
 
                             
                             本文对比了XML和TLV两种打包解包方式的性能,通过实验得出TLV在打包解包速度上远超XML,适用于对性能要求较高的场景。
本文对比了XML和TLV两种打包解包方式的性能,通过实验得出TLV在打包解包速度上远超XML,适用于对性能要求较高的场景。
           
       
           
                 
                 
                 
                 
                 
                
               
                 
                 
                 
                 
                
               
                 
                 扫一扫
扫一扫
                     
                     
              
             
                   1777
					1777
					
 被折叠的  条评论
		 为什么被折叠?
被折叠的  条评论
		 为什么被折叠?
		 
		  到【灌水乐园】发言
到【灌水乐园】发言                                
		 
		 
    
   
    
   
             
					 
					 
					


 
            