TLV是一种可变格式,意思就是:
Type类型, Lenght长度,Value值;
Type和Length的长度固定,一般那是2、4个字节(这里统一采用4个字节);
Value的长度有Length指定;
编码方法:
1. 将类型type用htonl转换为网络字节顺序,指针偏移+4
2. 将长度length用htonl转换为网络字节顺序,指针偏移+4
3. 若值value数据类型为int、char、short,则将其转换为网络字节顺序,指针偏移+4;若值为字符串类型,写进后,指针偏移+length
……继续处理后面的tlv;
解码方法:
1. 读取type 用ntohl转换为主机字节序得到类型,指针偏移+4
2. 读取lengh用ntohl转换为主机字节序得到长度;指针偏移+4
3. 根据得到的长度读取value,若value数据类型为int、char、short,用ntohl转换为主机字节序,指针偏移+4;若value数据类型为字符串类型,指针偏移+length
……继续处理后面的tlv;
类型(Type)字段是关于标签和编码格式的信息;
长度 (Length)字段定义数值的长度;
内容(Value)字段表示实际的数值。
因此,一个编码值又称TLV(Type,Length,Value)三元组。编码可以是基本型或结构型,如果它表示一个简单类型的、完整的显式值,那么编码就是基本型 (primitive);如果它表示的值具有嵌套结构,那么编码就是结构型 (constructed)。
下面是我写的一个Demo程序:
#include <stdio.h> #include <WinSock2.h> #include <string> #pragma comment(lib, "WS2_32") enum emTLVNodeType { emTlvNNone = 0, emTlvNRoot, //根节点 emTlvName, //名字 emTlvAge, //年龄 emTlvColor //颜色 1 白色 2 黑色 }; typedef struct _CAT_INFO { char szName[12]; int iAge; int iColor; }CAT_INFO,*LPCAT_INFO; 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”的长度 */ int TLV_EncodeCat(LPCAT_INFO pCatInfo, char *pBuf, int &iLen) { if (!pCatInfo || !pBuf) { return -1; } CTlvPacket enc(pBuf,iLen); enc.WriteInt(emTlvNRoot); enc.WriteInt(20+12+12); //根节点emTlvNRoot中的L,20=4+4+12,12=4+4+4,12=4+4+4 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; //总长度再加上emTLVNRoot的T和L,8=4+4 return 0; } int TLV_DecodeCat(char *pBuf, int iLen, LPCAT_INFO pCatInfo) { if (!pCatInfo || !pBuf) { return -1; } CTlvPacket encDec(pBuf,iLen); int iType; int iSum,iLength; encDec.ReadInt(&iType); if (emTlvNRoot != iType) { return -2; } 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 0; } int main(int argc, char* argv[]) { int iRet, iLen; char buf[256] = {0}; CAT_INFO cat; memset(&cat,0,sizeof(cat)); strcpy(cat.szName,"Tom"); cat.iAge = 5; cat.iColor = 2; iRet = TLV_EncodeCat(&cat,buf,iLen); if ( 0 == 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 ( 0 == iRet ) { printf("TLV_DecodeCat ok, cat name = %s, age = %d, color = %d. \n",cat.szName,cat.iAge,cat.iColor); } else { printf("TLV_DecodeCat error, code = %d. \n", iRet); } int iWait = getchar(); return 0; }
本Demo程序在VC2005环境下编译通过,下面是运行结果截图