TLV 格式及編解碼示例
阿新 • • 發佈:2019-02-20
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環境下編譯通過,下面是執行結果截圖