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代码如下:

 

  1 #include <stdio.h>
  2 #include <WinSock2.h>
  3 #include <string>
  4 
  5 #pragma comment(lib, "WS2_32")
  6 
  7 
  8 //定义枚举类型常量,来填充Tpye字段,其中emTlvNRoot填充根TLV包的Type
  9 //emTlvName字段用于填充子TLV字段中名字的Type字段.emTlvAge,emTlvColor类似
 10 //此类型字段是为了TLV包解码时识别到底是哪个TLV包.进而解析出对应的数据.
 11 enum emTLVNodeType
 12 {
 13     emTlvNNone = 0,
 14     emTlvNRoot,            //根节点
 15     emTlvName,            //名字
 16     emTlvAge,            //年龄
 17     emTlvColor            //颜色 1 白色 2 黑色
 18 };
 19 
 20 
 21 
 22 //定义要封装成TLV包的数据,包括名字,年龄,颜色。
 23 typedef struct _CAT_INFO
 24 {
 25     char szName[12];
 26     int    iAge;
 27     int iColor;
 28 }CAT_INFO,*LPCAT_INFO;
 29 
 30 
 31 //此类为TLC类,其中有四个成员函数,WriteInt和Write是用于
 32 //把原始数据封装为TLV包然后存入内存区块.即TLV包编码过程
 33 //而ReadInt和Read用于把内存区块的TLV包解析出来.即为TLV包的解码过程
 34 class CTlvPacket
 35 {
 36 
 37 public:
 38 
 39     CTlvPacket(char *pBuf,unsigned int len):
 40       m_pData(pBuf),m_uiLength(len),m_pEndData(m_pData+len),
 41           m_pWritePtr(m_pData),m_pReadPtr(m_pData) { }
 42 
 43     ~CTlvPacket() { }
 44 
 45 
 46     bool WriteInt(int data,bool bMovePtr = true)
 47     {
 48         int tmp = htonl(data);
 49         return Write(&tmp,sizeof(int));
 50     }
 51 
 52 
 53     bool Write(const void *pDst,unsigned int uiCount)
 54     {
 55         ::memcpy(m_pWritePtr,pDst,uiCount);
 56         m_pWritePtr += uiCount;
 57         return m_pWritePtr < m_pEndData ? true : false;
 58     }
 59 
 60 
 61     bool ReadInt(int *data,bool bMovePtr = true)
 62     {
 63         Read(data,sizeof(int));
 64         *data = ntohl(*data);
 65         return true;
 66     }
 67 
 68 
 69     bool Read(void *pDst,unsigned int uiCount)
 70     {
 71         ::memcpy(pDst,m_pReadPtr,uiCount);
 72         m_pReadPtr += uiCount;
 73         return m_pReadPtr < m_pEndData ? true : false;
 74     }
 75 
 76 
 77 private:
 78 
 79     char *m_pData;
 80 
 81     unsigned int m_uiLength;
 82 
 83     char *m_pEndData;
 84 
 85     char *m_pWritePtr;
 86 
 87     char *m_pReadPtr;
 88 
 89 };
 90 
 91 /*
 92 
 93 格式:
 94     root L1 V
 95         T L V T L V T L V
 96 
 97     L1 的长度即为“T L V T L V T L V”的长度
 98 
 99 */
100 
101 
102 //此函数实现TLV编码过程
103 int TLV_EncodeCat(LPCAT_INFO pCatInfo, char *pBuf, int &iLen)
104 {
105 
106     if (!pCatInfo || !pBuf)
107     {
108         return -1;
109     }
110 
111 
112     CTlvPacket enc(pBuf,iLen);
113     enc.WriteInt(emTlvNRoot);
114     enc.WriteInt(20+12+12); //length
115 
116     enc.WriteInt(emTlvName);
117     enc.WriteInt(12);
118     enc.Write(pCatInfo->szName,12);
119 
120     enc.WriteInt(emTlvAge);
121     enc.WriteInt(4);
122     enc.WriteInt(pCatInfo->iAge);
123 
124     enc.WriteInt(emTlvColor);
125     enc.WriteInt(4);
126     enc.WriteInt(pCatInfo->iColor);
127 
128 
129     iLen = 8+20+12+12;
130 
131     return 0;
132 
133 }
134 
135 //此函数实现TLV解码过程
136 int TLV_DecodeCat(char *pBuf, int iLen, LPCAT_INFO pCatInfo)
137 {
138 
139     if (!pCatInfo || !pBuf)
140     {
141         return -1;
142     }
143 
144 
145     CTlvPacket encDec(pBuf,iLen);
146     int iType;
147     int iSum,iLength;
148 
149 
150     encDec.ReadInt(&iType);
151     if (emTlvNRoot != iType)
152     {
153         return -2;
154     }
155     encDec.ReadInt(&iSum);
156 
157 
158     //通过判断Type头字段对TLV包进行解析
159     while (iSum > 0)
160     {
161 
162         encDec.ReadInt(&iType);//读取主TLV包的type头
163         encDec.ReadInt(&iLength);//读取主TLV包的length头
164 
165         switch(iType)        //此时buff指针移动到子TLV包.并解析子TLV的type头字段
166         {
167 
168         case emTlvName:
169             encDec.Read(pCatInfo->szName,12);
170             iSum -= 20;
171             break;
172 
173         case emTlvAge:
174             encDec.ReadInt(&pCatInfo->iAge);
175             iSum -= 12;
176             break;
177 
178         case emTlvColor:
179             encDec.ReadInt(&pCatInfo->iColor);
180             iSum -= 12;
181             break;
182 
183         default:
184             printf("TLV_DecodeCat unkonwn error. \n");
185             break;
186 
187         }
188 
189     }
190 
191     return 0;
192 
193 }
194 
195 //主函数
196 int main(int argc, char* argv[])
197 {
198 
199     int iRet, iLen;
200     char buf[256] = {0};
201 
202 
203     CAT_INFO cat;  //cat为定义的原始数据包括name,age,color
204     memset(&cat,0,sizeof(cat));//cat结构体初始化
205 
206     //对cat对象赋值
207     strcpy(cat.szName,"Tom");
208     cat.iAge = 5;
209     cat.iColor = 2;
210 
211     //实现对cat对象的编码,编码结果存储在buf中.
212     iRet = TLV_EncodeCat(&cat,buf,iLen);
213 
214 
215     //TLV编码成功与否的判断
216     if ( 0 == iRet )
217     {
218         printf("TLV_EncodeCat ok, iLen = %d. \n",iLen);
219     }
220     else
221     {
222         printf("TLV_EncodeCat error \n");
223     }
224 
225     //将cat结构置为0
226     memset(&cat,0,sizeof(cat));
227 
228    //TLV包解码过程,将解包后的数据存入cat结构体对象
229     iRet = TLV_DecodeCat(buf,iLen,&cat);
230 
231 
232     //输出解包后的结构体数据
233     if ( 0 == iRet )
234     {
235         printf("TLV_DecodeCat ok, cat name = %s, age = %d, color = %d. \n",cat.szName,cat.iAge,cat.iColor);
236     }
237     else
238     {
239         printf("TLV_DecodeCat error, code = %d. \n", iRet);
240     }
241 
242 
243     int iWait = getchar();
244 
245     return 0;
246 }

运行结果截图如下:


 

这里要对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

 

 

 

posted @ 2014-07-10 18:26  vpoet  阅读(2450)  评论(0编辑  收藏  举报