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环境下编译通过,下面是运行结果截图