赞
踩
TLV即tag length value,广义上来讲他并不是一个固定格式的协议,他可以是人们自己定义的用来网络通讯的协议,只有遵循了定义人的装包解包流程才能建立通讯。
从应用层HTTP协议,到超文本置标语言HTML(HyperText Mark-up Language),再到可扩展置标语言XML(Extensible Markup Language),它们提供了数据的格式化存储、传输和格式化显示的规范,是网络通信的基石。然而HTTP协议以及HTML/XML置标语言的本质就是定义一堆标签(Tag)对数据进行串行化序列化,然后接收方再根据标签解析、还原数据。
自定义通信协议的关键是对数据包的合理构造(construct)和正确解析(parse),即制定编解码规则。
抽象语法标记ASN(Abstract Syntax Notation) BER的长度确定的编码方式,由3部分组成Identifier octets、Length octets和Contents octets,实际上这就是一中TLV(Type-Length-Value)模型:类型字段(Type或Tag)是关于标签和编码格式的信息;长度字段(Length)定义数值的长度; 内容字段(Value)表示实际的数值。
因此,一个编码值又称TLV三元组。编码可以是基本型或结构型,如果它表示一个简单类型的、完整的显式值,那么编码就是基本型(primitive);如果它表示的值具有嵌套结构,那么编码就是结构型 (constructed)。
TLV编码就是指对Type(Tag)、Length和Value进行编码,形成比特流数据包;解码是编码的逆过程,是从比特流缓冲区中解析还原出原始数据。
那么TLV只是应用层协议,我们还需要不同层面的协议,比如传输层的TCP/UDP协议,这里就不细说。
字节流可用于任何类型的对象,包括二进制对象,而字符流只能处理字符或者字符串
实现温度打包网络传输,再解包加存入数据库
定义一个包 分为 tag datalenth value , value分为 id time temperature,三个值分别用分隔符隔开,方便解析。
客户端流程图(ps:第一次画图。。画的不太好)
client代码
pack_buf[pointer] = THE_HEAD;// head pointer += 1; pack_buf[pointer] = LHJ; pointer += 1; datalen = strlen(id_buf)+6+2+2; pack_buf[pointer] = datalen;//lenth pointer += 1; datalen = strlen(id_buf); memcpy(pack_buf+pointer, id_buf, datalen); pointer += datalen;//value pack_buf[pointer] = '|'; pointer += 1; /* time part */ int byte = get_sys_time(time_buf); datalen = byte; //memcpy(pack_buf+pointer, time_buf, datalen); for(int i=0;i<datalen;i++,pointer++) pack_buf[pointer] = time_buf[i]; /* temper part */ pack_buf[pointer] = '|'; pointer += 1; float temper = ds18b20_get_temper(); int temper_p1; int temper_p2; temper_p1 = (int)temper;// integer 1 byte if(temper_p1 > temper) temper_p1 -= 1; temper_p2 = (int)((temper-temper_p1)*100);// decimal 1 byte if((sizeof(pack_buf)-pointer) < (2+2)) { printf("have no more space to put temperature!\n"); return 1; } pack_buf[pointer] = temper_p1; pointer += 1; pack_buf[pointer] = temper_p2; pointer += 1; crc16 = crc_itu_t(MAGIC_CRC, pack_buf, pointer);//CRC ushort_to_bytes(&pack_buf[pointer], crc16); pointer += 2; pack_len = pointer; send_temper(pack_buf, fd,pack_len); pointer = 0; sleep(time_space);
server流程图
下图是虚线框的细节处理
server代码
int get_true_msg(int *flag,char buf[SIZE],char true_buf[SIZE]) { int len = 0; int byte = 0; int crc = 0; int crc_ago = 0; for(int i=0;i<SIZE;i++) { if(buf[i] == 253) { if(*flag-i < MINSIZE)//if leave space < minsize { memmove(buf, &buf[i], *flag-i); *flag = *flag-i; printf("get incomplete packet.\n"); break; } else if(buf[i+1] == 170)//tag { len = buf[i+2]; printf("%d\n",len); if(len+5 > *flag-i)//the message is incomplete { memmove(buf, &buf[i], *flag-i); *flag = *flag - i; printf("get incomplete packet.\n"); break; } else//the message is complete and we will confirm if the crc right { crc = crc_itu_t(MAGIC_CRC, &buf[i], len+3); crc_ago = bytes_to_ushort(&buf[i+3+len],2); if(crc != crc_ago) { printf("crc is not match."); continue; } else { memmove(true_buf,&buf[i+3],len); byte += len; continue; } } } } *flag = 0; return byte; } }
那么对包的解析要做到没有瑕疵,即所有的可能性都包括。代码后面我都标识了。
flag是buf读到的位置,函数中对flag的处理是将flag指向下一次将read的位置这样才能将不完整的消息补齐。
处理成功后将有用的字节放进true buf中,在根据分隔符拿出不同的数据,进行处理。这样解包就完成了。
这里还用到了memmove,这个函数在memcpy上做了改进,移一段buf到另一块区域时,如果目的区域与源区域有重叠,则会出现错误,这个时候memmove就能避免错误。
最后我们能成功解析出数据来
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。