socket通信之TLV传输协议

  • 了解TLV协议

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 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值