zoukankan      html  css  js  c++  java
  • 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. 
    ");
    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. 
    ",iLen);
    219     }
    220     else
    221     {
    222         printf("TLV_EncodeCat error 
    ");
    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. 
    ",cat.szName,cat.iAge,cat.iColor);
    236     }
    237     else
    238     {
    239         printf("TLV_DecodeCat error, code = %d. 
    ", 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

     

     
  • 相关阅读:
    .netcore3.1添加swagger及JWT Authorize 验证
    Quartz.Net实现简单定时任务调度
    基于.Net Framework进行配置Swagger
    升级anaconda的python版本命令
    使用jmeter自带的html查看测试报告
    软件质量模型的6大特性和27个子特性
    PyQt5基础教程及官方文档
    使用jmeter进行下载并发测试
    selenium官方入门文档
    web driver 驱动配置位置
  • 原文地址:https://www.cnblogs.com/vpoet/p/4659746.html
Copyright © 2011-2022 走看看