zoukankan      html  css  js  c++  java
  • Protocol buffer序列化及其在微信蓝牙协议中的应用

           Protocol buffer是Google出品的一种轻便高效的结构化数据存储格式,可对结构化数据进行序列化,并具有语言无关、平台无关等特点。在通信协议和数据存储等领域已经得到广泛的应用。

    眼下其已经提供 C/C++、Java、Python 等语言的 API。

    一、Protocol buffer和XML

    在数据通信传输时,一般须要将结构化的数据序列化成流进行传送。接收方再反序列化为原始格式数据进行处理。

    在Web通信领域,XML应用算是最通用的了。

    在时间性能上,尽管XML的序列化开销还可以,可是反序列化的效率是比較差。而Protocol buffer的反序列化效率是比較高的。在空间性能上,Protobuffer採用了可变长的数据编码格式,比XML的字符格式要高效得多。Google集群要处理PB级数据,Protocolbuffer可以在时间和空间性能上有所改进,在总体效益而言就是相当可观的。

    正是基于以上原因,微信和蓝牙外设的通信协议採用了Protocol buffer对消息包体进行打包。本文的目标是讲述Protobuffer在蓝牙微信协议中的应用,有助于理解微信蓝牙协议,对微信发给蓝牙外设的消息数据流进行反序列化,得到原始的结构化格式数据。因此语法也是环绕微信蓝牙协议包展开。更加具体的语法请百度相关内容或者找笔者探讨。

    二、微信蓝牙外设协议通用格式

    微信蓝牙使用流进行传输,在流上传输的是一个接着一个的业务逻辑的数据包。把设备发往厂商server或者微信server的请求包称为Req,回复包称为Resp,一个请求相应一个回包。

    把厂商server或者微信server主动发往设备的请求包称为PushReq。

    包结构由定长包头和变长包体组成,当中包体即由Protocol buffer进行打包。


    当中,定长包头为8个字节,bMagicNumber为0xFE;bVer为版本号01。nLength为包长。包含包头和包体的长度。nCmdId为命令编码,如登陆授权,初始化,发送数据。push推送等等。nSeq为序列号,PushReq包的序列号固定为0,其它请求和回复包的序列号务必保持一致,每次请求后序列号加1.

    以厂商server主动发数据给设备的PushReq包为例来说明变长包体,其相应定长包头的nCmdId为ECI_push_recvData = 30001。

    包体包含下面三个部分:

    1) Push包标识

    2) 自定义数据,指的是业务层自定义的数据格式形成的数据。

    3)  数据类型,如厂商自己定义的数据,还是微信clientHtml5的数据等

    三、Protobuffer的语法表述

    Protobuffer对push_recvData包的表述例如以下:


    Message类似C语言的struct数据结构,是Protobuffer消息定义的keyword。RecvDataPush是消息的名称。

    Required前缀表示该字段必须在序列化之前赋值。optional前缀表示能够不赋值。

    BasePush BasePush是message的第一个字段field。前者代表数据类型。后者是名称,当中BashPush在协议中是这样定义的:

    Message BashPush{} 其意味着里面没有数据项,BashPush在打包过程中仅仅会出现数据类型,事实上也是代表着一种push标识。

    Bytes Data是第二个字段field,为字符串。名称是Data。

    EmDeviceDataType type是第三个字段field。为设备数据类型。当中EmDeviceDataType是一个enum类型。例如以下:


    1,2,3代表字段在序列化后布局中的位置index,第一个位置是BashPush,接着是Data、type。

    四、Protobuffer打包

    Protobuffer打包有下面要素和规则:

    1.使用Varints算法表示数字。Varints是一种紧凑表示数字的方法。Varints中每个字节的最高位是有特殊含义的,假设是1,则表示兴许的字节也是该数字的一部分。假设是0,则结束。所以假设表示小于128的数值能够用一个字节,假设大于等于128的数字则要用很多其它的字节进行表示。

    2. Protobuffer有下面数据类型


    Type表示相应数据类型序号。当中Varint相应的数据类型包含int32,int64,enum等等。type序号为0,其都使用Varints进行表示。String,bytes,message类型相应的type序号为2.

    3. Protobuffer打包即是将message通过一系列的key-value对来表示。

    而key就是每一个message中各字段的index(并做一定运算)。value依据类型的不同会有不同的表现形式。

    当中key = field<<3 | type。

    field即字段的index。而type是字段的数据类型序号。

    而value,在数据类型为Varint时,直接为字段的赋值,依照Varints算法进行编码;在其它类型时就是“长度+原始内容编码”。

    五、PushReq包分析

    我们通过微信提供的AirSyncDebugger2.1.0.apk来分析Protobuffer对数据包的序列化。该APP用于微信和蓝牙外设的通信调试,其封装了微信蓝牙的协议,一般先用该APP调通蓝牙协议,再和后台server联调。如果我们自己定义的消息内容为fe cf 00 01 00 0c 20 01 00 00 00 00,即相应第二个字段bytesData。该消息内容是上一篇文章中server控制亮灯所发的消息。后台server和蓝牙外设的消息协议是自己定义的。

    AirSyncDebugger对PushReq包的序列化例如以下:


    序列化过程分析例如以下:

    固定包头(不受Protobuffer控制):

    Magic : fe

    Version: 01

    Length : 00 1A,即包体和包体的总长度为26字节。

    Cmdid : 75 31。十进制就是30001,即ECI_push_recvData包

    Seq : 00 00 push包的序列号都是00 00

    变长包体(Protobuffer控制打包,十六进表示):

    0A: BasePush的field是1, 值类型message的序号type为2,所以是0x1<<3|0x02 = 0x0A

    00 : 即值长度为0。BasePush值为空,事实上就是一种标识。

    12:data的field是2,值类型bytes的序号为2,所以是0x2<<3|0x2=0x12

    0C:  data的长度是12

    Fe cf 00 01 00 0c20 03 00 00 00 00 : data的内容。即我们自己定义的消息。

    18: Type的field是3,值类型enum的序号为0,所以是0x3<<3|0=0x18

    00:Type的值。由于enum属于Varint的一种,所以不须要长度。直接用值表示,0代表厂商自己定义数据。

    六、基于微信硬件公众平台的智能控制方案开发专栏介绍

           接下来嵌入式企鹅圈会将陆续公开基于微信硬件公众平台的智能控制开发技术细节。大致内容包含:

    1. 物联网架构和场景分析(已发)

    2. 基于微信硬件公众平台的智能控制开发流程(已发)

    3. 云server搭建和公众号配置

    4. 公众号菜单设置

    5. 微信消息传递过程和微信设备接入接口协议

    6. 微信硬件平台后台服务开发

    7. 微信蓝牙协议和授权、绑定过程

    8.Protocol buffer序列化及其在微信蓝牙协议中的应用(已发)

    9. 蓝牙外设控制开发

     

    谢谢支持嵌入式企鹅圈:


  • 相关阅读:
    ACM: SCU 4440 Rectangle
    ACM: NBUT 1646 Internet of Lights and Switches
    ACM: Long Live the Queen
    ACM: Racing Gems
    C++ 11 笔记 (一) : lambda
    cocos2d-x笔记2: 编译到安卓的步骤与注意事项
    C++笔记1: 单例模式。(一个简单的设计模式在C++中复杂出翔。。)
    Java笔记2 : 泛型的体现,及其上限、下限、通配符
    我终于忍不住喷一下某些书了,关于Java传引用的XX言论
    Java笔记1 : 在生产者消费者模式中,线程通信与共享数据,死锁问题与解决办法
  • 原文地址:https://www.cnblogs.com/gccbuaa/p/6835245.html
Copyright © 2011-2022 走看看