网络socket编程——TLV格式及编解码示例

前言:之前做过的网络socket上报温度是基于TCP。和UDP.HTTP一样都是通信协议,也就是通信时所遵守的规则,只有双方按照这个规则“说话”,对方才能理解或为之服务。这里说的协议,都是基于应用层的协议,一切应用层协议说白了,就是数据包的结构定义。至于TLV格式,就是Type-Length-Value ,数据包里放数据,要么用定长,要么用TLV这种可变长度的。同时TLV是一种通用的socket网络协议数据格式。具有扩展兼容性好,解析快,结构性好,数据包小的特点。

一:TCP HTTP UDP三者的关系:
TCP/IP是个协议组,可分为四个层次:网络接口层、网络层、传输层和应用层。
在网络层有IP协议、ICMP协议、ARP协议、RARP协议和BOOTP协议。
在传输层中有TCP协议与UDP协议。
在应用层有FTP、HTTP、TELNET、SMTP、DNS等协议。
因此,HTTP本身就是一个协议,是从Web服务器传输超文本到本地浏览器的传送协议。

二:socket:
这是为了实现以上的通信过程而建立成来的通信管道,其真实的代表是客户端和服务器端的一个通信进程,双方进程通过socket进行通信,而通信的规则采用指定的协议。socket只是一种连接模式,不是协议,tcp,udp,简单的说(虽然不准确)是两个最基本的协议,很多其它协议都是基于这两个协议如,http就是基于tcp的,.用socket可以创建tcp连接,也可以创建udp连接,这意味着,用socket可以创建任何协议的连接,因为其它协议都是基于此的。

三:数据包TLV的设计
从应用层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进行编码,形成比特流;解码是编码的逆过程,是从比特流缓冲区中解析还原出原始数据。

参考博客:https://blog.csdn.net/phunxm/article/details/5086080

客户端数据装包思路:
网络socket编程——TLV格式及编解码示例_第1张图片
当然中间的T.L.V可以自己随意定义,这个根据自己的实际需求来。
一张图大家应该能看明白:
ID:LSJ 3bytes
时间:原本应该为2019-04-18 21:52:37 2019=119(+1900)
数据包: 119 4 18 21 52 37 6bytes
温度:原本应该为19.56℃
数据包:19 56 2bytes
装包过程明白了原理就很好写
网络socket编程——TLV格式及编解码示例_第2张图片
装包代码:

#define HEAD 0xFD
#define ID 0X01
#define INT_TIME 0X02
#define FLOAT_TEMP 0X03

int makepack(char *buf,int size)
{
    int id_len=0;
    int data_len=0;
    int time_len=0;
    int temp_len=0;
    int buf_place=0;
    int  i;
    char *id="lsj";
        char datatime[32];
        float temp;
        unsigned short crc16=0;
    
        if(!buf || size<18)
        {
            printf("Invalid input arguements\n");
            return 0;
        }
        buf[buf_place]=HEAD;
        buf_place+=1;
    
        data_len=11;
        buf[buf_place]=data_len;
        buf_place+=1;
    
        buf[buf_place]=ID;
        buf_place+=1;
        id_len=strlen(id);
        memcpy(&buf[buf_place],id,id_len);
        buf_place+=id_len;
        
        buf[buf_place]=INT_TIME;
        buf_place+=1;
        memset(datatime,0,sizeof(datatime));
        int time=getdatatime(datatime);
        time_len=time;
        printf("time_len is %d\n",time_len);
        for(int i=0;i

CRC校验代码直接网上找的,直接套用。没有深入去了解它。大家有兴趣可以去了解。

四:数据包解包
数据包解包的流程就是装包的逆过程,流程图如下:
网络socket编程——TLV格式及编解码示例_第3张图片
第一次使用yed画流程图,有点糙,哈哈哈
源代码如下:
这里使用了大量的memmove函数,大家可以去了解一下这个函数

#include "tlv.server.h"
#include "tlv.crc.h"

#define HEAD 0xFD
#define ID 0X01
#define INT_TIME 0X02
#define FLOAT_TEMP 0X03
static sqlite3 *db;

int dividepack(char *buf,int size)
{
     int     i;
     unsigned short crc16=0;
     char    user[32];
     int     year;
     int     month;
     int     day;
     int     hour;
     int     minute;
     int     second;
     int     temp_1;
     int     temp_2;
     int     rc;
     char    *db_name="TEMP.db";
     char    *error=0;
     char    *insert=(char*)malloc(256);
     char    *select="select * from TEMP;";
     char    *sql="CREATE TABLE IF NOT EXISTS TEMP(""ID INT PRIMARY KEY,"
                                                    "USER CHAR(10),"
                                                    "TIME CHAR(50),"
                                                    "TEMP CHAR(10));";

     memset(user,0,sizeof(user));

     if(!buf||size<=6)
     {
         printf("Invalid input arguements\n");
         return 0;
     }

flag1:if(size>6)
     {
    flag2:for(i=0;i5)
                 {
                     if(buf[i+1]==11)
                     {
                         if(buf[i+2]==ID)
                         {
                             memcpy(user,&buf[i+3],3);
                             printf("user is %s\n",user);
                             if(buf[i+6]==INT_TIME)
                             {
                                 year=buf[i+7];
                                 year+=1900;
                                 printf("year is %d\n",year);
                                 month=buf[i+8];
                                 printf("month is %d\n",month);
                                 day=buf[i+9];
                                 printf("day is %d\n",day);
                                 hour=buf[i+10];
                                 printf("hour is %d\n",hour);
                                 minute=buf[i+11];
                                 printf("minute is %d\n",minute);
                                 second=buf[i+12];
                                 printf("second is %d\n",second);
                                 if(buf[i+13]==FLOAT_TEMP)
                                 {
                                     temp_1=buf[i+14];
                                     printf("temp_1 is %d\n",temp_1);
                                     temp_2=buf[i+15];
                                     printf("temp_2 is %d\n",temp_2);
                                    
                                     crc16=crc_itu_t(MAGIC_CRC,buf,18);
                                     //ushort_to_bytes(&buf[i+16],crc16);
                                     if(crc16==0)
                                     {   
                                         printf("crc true\n");
                                         printf("divide pack finish\n");
                                         memmove(buf,&buf[i],size);size=0;
                                     }
                                     else
                                     {
                                         memmove(buf,&buf[i+16],size-i-16);size=size-i-16;goto flag1;
                                    }
                                 }
                                 else 
                                 {
                                    memmove(buf,&buf[i+13],size-i-13);size=size-i-13;goto flag1;
                                 }
                             }
                             else 
                             {
                                 memmove(buf,&buf[i+6],size-i-6);size=size-i-6;goto flag1;
                             }
                         }
                         else 
                         {
                             memmove(buf,&buf[i+2],size-i-2);size=size-i-2;goto flag1;
                         }
                     }
                         else
                         {
                             memmove(buf,&buf[i+1],size-i-1);size=size-i-1;goto flag1;
                         }
                    }
                 else 
                 {
                     memmove(buf,&buf[i],size-i);return size-i;goto flag1;
                 }
              }
             else
             {
                 goto flag2;
             }
         }
     }
     else 
     {
         return size;
     }

下面部分是数据库的内容

     rc=sqlite3_open(db_name,&db);
     if(rc!=SQLITE_OK)
     {
        fprintf(stderr,"sql error:%s\n",sqlite3_errmsg(db));
     }
     else
     {
         printf("open sqlite successfully!\n");
     }
    

     rc=sqlite3_exec(db,sql,NULL,NULL,&error);
     if(rc!=SQLITE_OK)
     {
         fprintf(stderr,"sql error:%s\n",error);
         sqlite3_close(db);
         sqlite3_free(error);
         exit(1);
     }
     else
     {
         printf("create table successfully!\n");
     }

     snprintf(insert,256,"insert into TEMP(USER,TIME,TEMP)values('%s','%04d-%02d-%02d %02d:%02d:%02d','%d.%d');",user,year,month,day,hour,minute,second,temp_1,temp_2);
     rc=sqlite3_exec(db,insert,NULL,NULL,&error);
     if(rc!=SQLITE_OK)
     {
         fprintf(stderr,"sql error:%s\n",error);
         sqlite3_close(db);
         sqlite3_free(error);
         exit(1);
     }
     else
     {
         printf("insert successfully!\n");
     }
     
     rc=sqlite3_exec(db,select,NULL,NULL,&error);
     if(rc!=SQLITE_OK)
     {
         fprintf(stderr,"sql error:%s\n",error);
         sqlite3_close(db);
         sqlite3_free(error);
         exit(1);
     }
     else
     {
         printf("select successfully!\n");
     }
     sqlite3_close(db);
}

后序:这篇博客重点在于理解tlv是干嘛的,有什么作用,以及如何运用它。还有要熟练掌握网络socket编程。

项目源码已上传码云:https://gitee.com/lsj123456/tlv.git

你可能感兴趣的:(socket编程)