TLV----Demo講解

接觸過網絡協議的人對TLV一定或多或少的知道.作爲一種自定義應用層標準.

TLV使用十分廣泛.他對數據封包有着很好的定義,簡單實用.



TLV即Type-Length-Value.即我們每個封裝成TLV包的數據都必須爲其添加Type和Length字段


TLV示意圖如下:




大家首先要區分數據包和數據報.本文的實例僅僅針對TLV數據包.

而並未添加註冊一些控制信令和報頭形成數據報,而其中實際的數據

有TLV包組成.


TLV數據包和數據報的關係可由下圖表示:





此外,TLV本身有兩種結構,一種是基本TLV結構,另外一種是嵌套TLV結構.而本文使用的是嵌套TLV結構.

基本TLV包:


嵌套TLV包:



本文使用嵌套TLV包.




幾點說明:

編碼方法

1. 將類型type用htonl轉換爲網絡字節順序,指針偏移+4

2. 將長度length用htonl轉換爲網絡字節順序,指針偏移+4

3. 若值value數據類型爲int、char、short,則將其轉換爲網絡字節順序,指針偏移+4;若值爲字符串類型,寫進後,指針偏移+length


解碼方法

1. 讀取type 用ntohl轉換爲主機字節序得到類型,指針偏移+4

2. 讀取lengh用ntohl轉換爲主機字節序得到長度;指針偏移+4

3. 根據得到的長度讀取value,若value數據類型爲int、char、short,用ntohl轉換爲主機字節序,指針偏移+4;若value數據類型爲字符串類型,指針偏移+length



Type和Length的長度固定,一般那是2、4個字節(這裏統一採用4個字節);

Value的長度有Length指定



Demo代碼如下:

#include <stdio.h>
#include <WinSock2.h>
#include <string>

#pragma comment(lib, "WS2_32")


//定義枚舉類型常量,來填充Tpye字段,其中emTlvNRoot填充根TLV包的Type
//emTlvName字段用於填充子TLV字段中名字的Type字段.emTlvAge,emTlvColor類似
//此類型字段是爲了TLV包解碼時識別到底是哪個TLV包.進而解析出對應的數據.
enum emTLVNodeType
{
	emTlvNNone = 0,
	emTlvNRoot,			//根節點
	emTlvName,			//名字
	emTlvAge,			//年齡
	emTlvColor			//顏色 1 白色 2 黑色
};



//定義要封裝成TLV包的數據,包括名字,年齡,顏色。
typedef struct _CAT_INFO
{
	char szName[12];
	int	iAge;
	int iColor;
}CAT_INFO,*LPCAT_INFO;


//此類爲TLC類,其中有四個成員函數,WriteInt和Write是用於
//把原始數據封裝爲TLV包然後存入內存區塊.即TLV包編碼過程
//而ReadInt和Read用於把內存區塊的TLV包解析出來.即爲TLV包的解碼過程
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, int &iLen)
{

	if (!pCatInfo || !pBuf)
	{
		return -1;
	}


	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 0;

}

//此函數實現TLV解碼過程
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);


	//通過判斷Type頭字段對TLV包進行解析
	while (iSum > 0)
	{

		encDec.ReadInt(&iType);//讀取主TLV包的type頭
		encDec.ReadInt(&iLength);//讀取主TLV包的length頭

		switch(iType)		//此時buff指針移動到子TLV包.並解析子TLV的type頭字段
		{

		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;  //cat爲定義的原始數據包括name,age,color
	memset(&cat,0,sizeof(cat));//cat結構體初始化

	//對cat對象賦值
	strcpy(cat.szName,"Tom");
	cat.iAge = 5;
	cat.iColor = 2;

	//實現對cat對象的編碼,編碼結果存儲在buf中.
	iRet = TLV_EncodeCat(&cat,buf,iLen);


	//TLV編碼成功與否的判斷
	if ( 0 == iRet )
	{
		printf("TLV_EncodeCat ok, iLen = %d. \n",iLen);
	}
	else
	{
		printf("TLV_EncodeCat error \n");
	}

	//將cat結構置爲0
	memset(&cat,0,sizeof(cat));

   //TLV包解碼過程,將解包後的數據存入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;
}



運行結果截圖如下:




這裏要對TLV包長:iLen = 8+20+12+12; 進行說明.

爲什麼是這樣呢.

因爲一個完整的TLV包的主包包含Type字段和Length字段,每個字段佔用4個字節所以共八個字節.

而主TLV包的Value字段包含三個子TLV包.

第一個子TLV包爲name,而char szName[12],加之子包Type和子包Length,所以一共20個字節

同理,對於age子包共計八個字節,color子包共計八個字節.

所以整個TLV包的長度就爲8+20+12+12



本文參考自博客:http://blog.csdn.net/chexlong/article/details/6974201



轉載請註明作者:小劉






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章