TLV协议是BER编码的一种,全称是Tag、length、value。该协议简单高效,能适用于各种通信场景,且具有良好的可扩展性。TLV协议的基本格式如下: 其中,Tag,是报文的唯一标识;Length,表示Value字段的长度;Value字段的数据是需要传输的数据,长度由Length字段表示。

只用TLV可能会出现问题

我们该如何理解TLV那,通信的双方一定要有一种规约,收到的数据该如何解析,比如我们在应用层A用户给B用户发数据: A------- 数据----------->B 数据在收发时,一定会存在三个非常重要的信息: 1、这是什么数据? Tag 2、数据有多少个字节? Length 3、数据的内容是什么? Value B收到A发来的信息,就开始按照这三个开始解析,是不是A发来的消息。但是只有TLV封装的话,A给B发送过程中,会出现许多差错,比如:

问题1:数据可能重合!如果你设置0x01为温度,那么后面有个0x01可能是表示数据的,而不是温度! 这时候我们需要加一个报头,固定为0xFD,用来标志一个报文的开始问题2:数据可能会跳变!你不能保证数据传输过程中数据的跳变,0x01表示温度,跳变为0x02就表示另外的东西了!

这时候我们需要在一个字节流末尾加一个CRC校验,传输前算一下字节流的大小,传输到另外一个端口后再算一下,对比前后两个CRC的值,如果相同,表示没有发生字节跳变,如果不同,那就舍弃!

问题3:报头可能在TLV中!

同样也要加CRC校验

加工后的TLV:

如果还有不清楚为什么要加报文头和CRC校验的同学,可以参考这篇博客https://blog.csdn.net/qq_43296898/article/details/88863854

为接下来我要完成网络socket实现温度上报,要用到自定义通讯协议TLV,我讲用TLV的格式将“ID/时间/温度“上报,如“RPI0001/2019-01-05 11:40:30/30.0C”; 根据上面介绍的我将把这个数据封装成TLV格式上报。这里面有三种数据要发送,所以就要写三个TLV报文。

TLV的封包代码示例

下面是我用c代码实现的TLV报文格式的发送信息:

#include

#include

#include "header.h"

#include "tlv.crc-itu-t.h"

#define HEAD 0xFD //定义一个报文头

#define BUF\_SIZE 128 //定义一个buffer的大小

#define TLV\_FIXED\_SIZE 5 //固定TLV字节流大小,不包含value的值

#define TLV\_MINI\_SIZE (TLV\_FIXED\_SIZE+1) //TLV字节流的最小值,value的最小值为1个字节

enum //使用枚举 Tag的值会自加

{

TAG_ACK = 1,

TAG_SN, //树莓派上的id

TAG_TEMP, //温度

TAG_TIME, //时间

};

int pack_ack (char \*buf , int size); //声明ack封装函数

int pack_sn (char \*buf , int size);//声明id封装函数

int pack_temp (char \*buf , int size);//声明温度封装函数

int pack_time (char \*buf , int size);//声明时间温度封装函数

int main (int argc, char \*\*argv)

{

char buf[BUF_SIZE];

int bytes; //一个封装函数的字节流的个数

bytes = pack_ack (buf , sizeof(buf));

dump\_buf("ack", buf, bytes);//设置dump\_buf函数,把所有的字节流都放到dump\_buf里面

bytes = pack\_sn(buf , sizeof(buf));

dump\_buf("sn", buf, bytes);

bytes = pack\_temp(buf , sizeof(buf));

dump\_buf("temp", buf, bytes);

bytes = pack_time (buf , sizeof(buf));

dump\_buf("time", buf, bytes);

return 0;

}

/\*树莓派的编号ID=RPI0001报文

\* H T L Value CRC1 CRC2

\* 0xFD 0x01 0x01 0x07 0x12 0x34

\* \*/

int pack\_sn(char \*buf, int size)//声明id封装函数

{

unsigned short crc16 = 0;

char \*id = "RPI0001";

int ofset = 0;

int pack_len = 0;

int data_len = 0;

if(!buf || size < TLV_MINI_SIZE)

{

printf("Invalid input argument \n");

return 0;

}

buf[ofset]=HEAD;

ofset+=1;

buf[ofset]=TAG_SN;

ofset +=1;

data_len=strlen(id);

pack_len = data_len + TLV_FIXED_SIZE;

buf[ofset]=pack_len;

ofset +=1;

memcpy(&buf[ofset], id, data_len);

ofset += data_len;

crc16=crc\_itu\_t(MAGIC_CRC, buf, ofset);

ushort\_to\_bytes(&buf[ofset], crc16);

ofset +=2;

return ofset; //返回索引值

}

/\* 树莓派上获取温度30.5C报文

\* H T L Value CRC1 CRC2

\* 0xFD 0x03 0x00 0x14 0x3A 0x12 0x34

\*/

int pack\_temp(char \*buf, int size)

{

unsigned short crc16=0;

int pack_len = 0;

int data_len = 0;

int ofset = 0;

float temp;

char datatime[32];

int i,j;

if(!buf || size < TLV_MINI_SIZE)

{

printf("Invalid input adgument\n");

return 0;

}

buf[ofset]=HEAD;

ofset +=1;

buf[ofset]=TAG_TEMP;

ofset +=1;

ds18b20\_get\_temperature(&temp);

i=(int)temp; //整数部分

j=(int) ((temp-i) \*100);//小数部分

data_len = sizeof(temp);

pack_len= data_len + TLV_FIXED_SIZE;

buf[ofset]=pack_len;

ofset +=1;

buf[ofset]=i;

ofset+=1;

buf[ofset]=j;

ofset+=1;

crc\_itu\_t(MAGIC_CRC, buf, ofset);

ushort\_to\_bytes(&buf[ofset], crc16);

ushort\_to\_bytes(&buf[ofset], crc16);

ofset +=2;

return ofset;//返回索引值

}

/\* 获取系统时间的报文 2020-4-12 23:22:00

\* H T L Value

CRC1 CRC2

\* 0xFD 0x04 0x06 0x14 0x04 0x0c 0x17 0x16 0x00 0x12 0x34

\* \*/

int pack_time (char \*buf, int size)

{

unsigned short crc16=0;

int ofset = 0;

int pack_len = 0;

int data_len = 0;

char datatime[32];

char \*datime;

if( !buf || size < TLV_MINI_SIZE)

{

printf("Invald input argument\n");

return 0;

}

buf[ofset]=HEAD;

ofset+=1;

buf[ofset]=TAG_TIME;

ofset +=1;

memset(datatime, 0, sizeof(datatime));

int time=get\_sys\_time(datatime);

data_len = time;

pack_len =data_len + TLV_MINI_SIZE;

buf[ofset]=pack_len;

ofset+=1;

for(int i=0; i< data_len; i++, ofset++)

buf[data_len]=datatime[i];

crc16=crc\_itu\_t(MAGIC_CRC,buf, ofset);

ushort\_to\_bytes(&buf[ofset], crc16);

ofset +=2;

return ofset;//返回索引值

}

void dump\_buf(char \*type, char \*data, int len)//data指针,指向buf的首地址,len是buf的长度

{

if(type)

{

printf("%s:\n", type);

}

int i;

for(i=0; i

{

printf("0x%02x\n",data[i]);