TLV :即Tag(Type)-Length-Value,它包含三个域,第一个Tag为要封装的数据的类型域,第二个Length为封装的总的数据长度,第三个Value为要装入数据的值。其实就是一个简单的自定义通信协议,将要传送的数据进行编码组织将其发送出去的过程。想要了解的更细致,建议自己可以百度搜索学习,这里就不细述了。下面就以一个例子说一下装包和解包。
数据装包
装包的过程其实很简单,只要将你的数据按照包头(PACK_HEADER)、Tag(Type)-Length-Value、校验和的顺序将每个字节按十六进制的形式装进去,形成一个字节流发送出去,如下图:
PACK_HEADER自己定义(我定义为0xFD),大小通常为一个字节;Tag也是自己定义,通常为一个字节;Length的大小要看你的数据所占用的字节大小,它的大小为包头大小+Tag大小+Length大小+校验和大小+数据字节大小,自身一般占用一个字节;crc校验和一般占两个字节。crc校验有兴趣可以自己学习,本人也不是很懂,用别人的源码。知道了怎么装包,下面来解释一下**为什么要加包头和校验和进去:**举一个例子,如果我们不加头和尾,要发送的数据为0x01 0x03 0x3c,接收端就会按照Tag(Type)-Length-Value顺序进行解析,知道数据的类型为01,长度为3,值为3c。但是发送的过程中也有可能数据丢失或者混入了其他数据,这时数据有可能会变为0x00 0x01 0x56 0x03 0x23,这时接收端按照之前的顺序解析就会出错,当我们加上头的时候,接收端解析的时候会先找到包头,然后按顺序解析,但是这样还是有出错的可那,但是加上crc校验和之后,crc根据接收到的数据计算出一个校验和,用来和装包装进去的校验和进行比较,如果相同说明这一帧数据正确,接收端才会开始解析数据,如果不正确就不解析。下面是我将获取到的温度进行装包的代码:
int packtlv_temp(char *buf,int size,float temp)
105 {
106 unsigned short crc16=0;
107 int packlen=0;
108 int ofset=0;
109 int data_len=2;
110
111 int i,j;
112 float a;
113 if( !buf || !temp || size<TLV_MIN_SIZE )
114 {
115 printf("Invalid input argumrnts\n");
116 return 0;
117 }
118 /*pack header*/
119 buf[ofset]=PACK_HEADER;
120 ofset+=1;
121 /*type*/
122 buf[ofset]=TAG_TEMP;
123 ofset+=1;
124 /*length*/
125 /*
126 if( sizeof(temp)<=size-TLV_FIXED_SIZE )
127 {
128 data_len=sizeof(temp);
129 }
130 else
131 {
132 data_len=size-TLV_FIXED_SIZE;
133 }
134 */
135
136 packlen=data_len+TLV_FIXED_SIZE;
137 printf("pack length:%d\n",packlen);
138
139 buf[ofset]=packlen;
140 ofset+=1;
141
142 i=(int)temp;/*整数部分*/
//printf("i=%d\n",i);
144 a=(temp-i)*100;/*小数部分*/
145 //printf("a=%f\n",a);
146 buf[ofset]=i;
147 ofset+=1;
148 buf[ofset]=a;
149 ofset+=1;
150
151
152 crc16 = crc_itu_t(MAGIC_CRC, buf, ofset);
153 ushort_to_bytes(&buf[ofset], crc16);
154 ofset += 2;
155 for(j=0;j<packlen;j++)
156 {
157 printf("0x%02x ",(unsigned char)buf[j]);
158
159 }
160 printf("\n");
161
162 return ofset;
163
164 }
数据解析
解析的过程没有装包那么容易,逻辑性很强而且过程很复杂,建议自己先画一个流程图,搞清解包时所有可能的情况,再来写代码。解包时首先要判断接收到的数据是否为空,在判断整个数据的长度是否比最小长度还要小,满足条件之后开始寻找头,遍历整个接收到数据,一直找到头再开始解析,解析的时候千万别以为就不会出错了,还要判断减去头后的大小是否小于2,小于2说明数据不完整,然后用crc校验,校验正确说明数据没问题可以解析。大概流程就是这样,完整的过程看流程图,如有大佬发现不完整,欢迎指正,共同学习。流程图如下:
下面是代码:
83 int unpacktlv(char *buf,int bv)
84 {
85
86 int i;
87 int tlv_len;
88 char *ptr=NULL;
89
90 float t=0.0;
91 float n,m;
92
93 unsigned short crc16;
94 unsigned short crc;
95
96 if( !buf )
97 {
98 printf("Invailed input!\n");
99 return 0;
100 }
101
102 if( buf==NULL )
103 {
104 printf("buf is NULL!\n");
105 return 0;
106 }
107 printf("buf is not NULL\n");
108 go_on:
109 if( bv<TLV_MIN_SIZE )
110 {
111 printf("buf 不完整\n");
112 return bv;
113 }
114 printf("buf is good!\n");
115
116 for(i=0;i<bv;i++)
117 {
118
119 if( (unsigned char)buf[i]!=PACK_HEADER )
120 {
121 continue ;
122 }
123 printf("\nFind header!\n");
124
125 if( bv-i<2 )
126 {
127 printf("can not find length...\n");
128 memmove(buf,&buf[i],bv-i);
129 bv=bv-i;
130 goto go_on;
131 }
132
133 ptr=&buf[i];
134 tlv_len=ptr[2];
135
136 printf("tlv length:%d\n",tlv_len);
137
138 if( tlv_len<TLV_MIN_SIZE || tlv_len>TLV_MAX_SIZE )
139 {
140 printf("tlv_len is error!\n");
141 memmove(buf,&ptr[2+1],bv-i-2);
142 bv=bv-i-2;
143 goto go_on;
144 }
145
146 printf("tlv_len is ture!\n");
147
148 if( tlv_len>bv-i )
149 {
150 printf("Error!\n");
151 memmove(buf,ptr,bv-i);
152 return bv-i;
153 }
154
155 crc16=crc_itu_t(MAGIC_CRC,(unsigned char*)ptr, tlv_len-2);
156 crc=bytes_to_ushort((unsigned char*)&ptr[tlv_len-2],2) ;
159 if( crc!=crc16 )
160 {
161 printf("crc is not ture!\n");
162 memmove(buf,&ptr[tlv_len],bv+tlv_len);
163 bv=bv+tlv_len;
164 goto go_on;
165 }
166 printf("crc is true!\n");
167
168 m=ptr[3];
169 n=((float)ptr[4])/100;
170 //printf("%d\n",ptr[4]);
171 //printf("0x%02x \n",(unsigned char)ptr[4]);
172 t=m+n;
173
174 printf("Temperature is:%f\n",t);
175 printf("\n");
176
177 memmove(buf,&ptr[tlv_len],bv-i-tlv_len);
178 bv=bv-i-tlv_len;
179 goto go_on;
180 }
181 return 0;
182 }