接触过网络协议的人对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 = ,
emTlvNRoot, //根节点
emTlvName, //名字
emTlvAge, //年龄
emTlvColor //颜色 1 白色 2 黑色
}; //定义要封装成TLV包的数据,包括名字,年龄,颜色。
typedef struct _CAT_INFO
{
char szName[];
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 -;
} CTlvPacket enc(pBuf,iLen);
enc.WriteInt(emTlvNRoot);
enc.WriteInt(++); //length enc.WriteInt(emTlvName);
enc.WriteInt();
enc.Write(pCatInfo->szName,); enc.WriteInt(emTlvAge);
enc.WriteInt();
enc.WriteInt(pCatInfo->iAge); enc.WriteInt(emTlvColor);
enc.WriteInt();
enc.WriteInt(pCatInfo->iColor); iLen = +++; return ; } //此函数实现TLV解码过程
int TLV_DecodeCat(char *pBuf, int iLen, LPCAT_INFO pCatInfo)
{ if (!pCatInfo || !pBuf)
{
return -;
} CTlvPacket encDec(pBuf,iLen);
int iType;
int iSum,iLength; encDec.ReadInt(&iType);
if (emTlvNRoot != iType)
{
return -;
}
encDec.ReadInt(&iSum); //通过判断Type头字段对TLV包进行解析
while (iSum > )
{ encDec.ReadInt(&iType);//读取主TLV包的type头
encDec.ReadInt(&iLength);//读取主TLV包的length头 switch(iType) //此时buff指针移动到子TLV包.并解析子TLV的type头字段
{ case emTlvName:
encDec.Read(pCatInfo->szName,);
iSum -= ;
break; case emTlvAge:
encDec.ReadInt(&pCatInfo->iAge);
iSum -= ;
break; case emTlvColor:
encDec.ReadInt(&pCatInfo->iColor);
iSum -= ;
break; default:
printf("TLV_DecodeCat unkonwn error. \n");
break; } } return ; } //主函数
int main(int argc, char* argv[])
{ int iRet, iLen;
char buf[] = {}; CAT_INFO cat; //cat为定义的原始数据包括name,age,color
memset(&cat,,sizeof(cat));//cat结构体初始化 //对cat对象赋值
strcpy(cat.szName,"Tom");
cat.iAge = ;
cat.iColor = ; //实现对cat对象的编码,编码结果存储在buf中.
iRet = TLV_EncodeCat(&cat,buf,iLen); //TLV编码成功与否的判断
if ( == iRet )
{
printf("TLV_EncodeCat ok, iLen = %d. \n",iLen);
}
else
{
printf("TLV_EncodeCat error \n");
} //将cat结构置为0
memset(&cat,,sizeof(cat)); //TLV包解码过程,将解包后的数据存入cat结构体对象
iRet = TLV_DecodeCat(buf,iLen,&cat); //输出解包后的结构体数据
if ( == 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 ;
}

运行结果截图如下:

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

 

TLV----Demo讲解的更多相关文章

  1. Swift轻松入门——基本语法介绍和详细地Demo讲解(利用WebView打开百度、新浪等网页)

    转载请务必注明出处(all copyright reserved by iOSGeek) 本文主要分为两个部分,第一部分介绍Swift的基本语法,第二部分讲解一个利用WebView来打开百度.sina ...

  2. 02——微信小程序官方demo讲解——app部分

    第一节讲了目录结构,这节主要讲解下目录中app.js部分. 它由三部分组成app.js.app.json与app.wxss 1.JS部分 1.1概述 //app.js App({ onLaunch: ...

  3. C# webApi 与 AngularJs 实现增删改Demo 讲解(一)

    公司在使用webAPI+AngularJs+SlcikGrid进行产品开发,自己也是初学Angular,就做了一个Demo,实现增删改功能,希望可以帮助大家. 界面如同所示:  数据库一张单表很简单, ...

  4. 支付宝即时到账接口开发 - DEMO讲解

    支付宝即时到帐接口 环境要求 PHP5.0以上,且需要开启curl.openssl. 文档地址: https://doc.open.alipay.com/doc2/detail?treeId=62&a ...

  5. 03——微信小程序官方demo讲解——page部分

    一个page由一个文件夹以及文件夹下四个文件组成. 比如一个页面叫index.则需要在pages目录下新建一个index目录,且包含由index+类型(js\wxml\wxss\json)为名组成的若 ...

  6. 01——微信小程序官方demo讲解——文件结构

    1.环境概览 首先环境配置的部分略过,打开小程序开发工具.选择一个空目录,即可开始一个demo项目. 其中新建成功后的目录如图所示: 2.文件结构描述 如图所示,左边是界面展示,右边是目录结构. 目录 ...

  7. WCF开发的流程-服务端和客户端之间的通讯(内含demo讲解)

    讲解技术之前,恳请博友让我说几句废话.今天是我第一在博客园发布属于自己原创的博文(如有雷同,那是绝对不可能的事,嘿嘿).之前一直是拜读各位博友的大作,受益匪浅的我在这对博友们说声谢谢,谢谢你们的共享! ...

  8. 分享-结合demo讲解JS引擎工作原理

    代码如下: var x = 1; function A(y){ var x = 2; function B(z){ console.log(x+y+z); } return B; } var C = ...

  9. 【SSH系列】初识spring+入门demo

    学习过了hibernate,也就是冬天,经过一个冬天的冬眠,当春风吹绿大地,万物复苏,我们迎来了spring,在前面的一系列博文中,小编介绍hibernate的相关知识,接下来的博文中,小编将继续介绍 ...

  10. RabbitMQ的使用Demo

    rabbitmq消息队列,官网有六种,实战常用的也就如下五种. 下面开始demo讲解 大致三步:1.配置消息队列,2.生产者提供消息给队列,3.消费者监听消费队列消息 源码下载:https://pan ...

随机推荐

  1. linux系统下,11款常见远程桌面控制软件

    linux系统下,11款常见远程桌面控制软件 一. Grdc 它是一个用GTK+编写的,适用于gnome桌面环境的远程桌面访问软件.看图: 常见功能: 1.提供全屏,窗口化的远程控制.支持高分辨率下的 ...

  2. VS2008生成的程序无法在其它电脑上运行,提示系统无法执行指定的程序

    经过一番查找,最给力的参考是 http://www.cnblogs.com/visoeclipse/archive/2010/02/27/1674866.html ------------------ ...

  3. 用Matlab完成:从第一个人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下的是原来第几号的那位。

    程序思路: (1)一圈人循环报数,报数报到3的人,将其置为0,表示被淘汰: (2)那么在接下去的循环中,被淘汰的人不参与报数: (3)直到仅有1人没被淘汰,并显示出他之前的编号. 程序实现如下: cl ...

  4. where 1=1

    sql: where 1=1 1=1 永真, 1<>1 永假. 1<>1 的用处: 用于只取结构不取数据的场合 例如: 拷贝表 create   table_name   as ...

  5. IOS Custom NavigationItem --写titleView

    //先自己写一个titleView UIView *titleView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 20)];//all ...

  6. 图像处理中像素点的问题:unsigned char 和 char

    以前在做图像处理的时候,一直不太在意这个问题,对图像每个像素点的灰度值,总是认为char也可,unsigned char也可.尽管它们都是8位,但是表示的数的范围却不相同:char: -128~127 ...

  7. unity 3d 生成apk文件时,设置图标

    如图所示:注意红线标注部分: 如果安装到手机之后,程序的图标还是unity的默认图标,重启手机即可.

  8. drupal 7.23 上传中文命名文件bug

    $file->filename = trim(drupal_basename($_FILES['files']['name'][$source]), '.'); //在此行下 添加以下代码即可( ...

  9. django配置

    安装python环境后,安装pip工具 通过pip下载安装django pip install django   django在web中的应用主要由两部分构成,工程与App 工程即相当于一下门户框架 ...

  10. 使用AsyncTask实现图片加载

    如上图所示:我们看到的就是使用PrograssDialog进度条和AsyncTask异步任务实现的效果(额,不要看应用名...).下面介绍一下具体的实现流程. 一.首先使用XML布局,布局很简单直接上 ...