1. 程式人生 > >TLV 格式及編解碼示例

TLV 格式及編解碼示例

 

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環境下編譯通過,下面是執行結果截圖