Protocol buffer是Google出品的一種輕便高效的結構化數據存儲格式,可對結構化數據進行序列化,並具備語言無關、平臺無關等特色,在通訊協議和數據存儲等領域已經獲得普遍的應用。目前其已經提供 C/C++、Java、Python 等語言的 API。算法
1、Protocol buffer和XML服務器
在數據通訊傳輸時,通常須要將結構化的數據序列化成流進行傳送,接收方再反序列化爲原始格式數據進行處理。在Web通訊領域,XML應用算是最通用的了。在時間性能上,雖然XML的序列化開銷還能夠,可是反序列化的效率是比較差,而Protocol buffer的反序列化效率是比較高的;在空間性能上,Protobuffer採用了可變長的數據編碼格式,比XML的字符格式要高效得多。Google集羣要處理PB級數據,Protocolbuffer可以在時間和空間性能上有所改進,在總體效益而言就是至關可觀的。微信
正是基於以上緣由,微信和藍牙外設的通訊協議採用了Protocol buffer對消息包體進行打包。本文的目標是講述Protobuffer在藍牙微信協議中的應用,有助於理解微信藍牙協議,對微信發給藍牙外設的消息數據流進行反序列化,獲得原始的結構化格式數據。所以語法也是圍繞微信藍牙協議包展開。更加詳細的語法請百度相關內容或者找筆者探討。數據結構
2、微信藍牙外設協議通用格式微信開發
微信藍牙使用流進行傳輸,在流上傳輸的是一個接着一個的業務邏輯的數據包。把設備發往廠商服務器或者微信服務器的請求包稱爲Req,回覆包稱爲Resp,一個請求對應一個回包。把廠商服務器或者微信服務器主動發往設備的請求包稱爲PushReq。架構
包結構由定長包頭和變長包體組成,其中包體即由Protocol buffer進行打包。佈局
其中,定長包頭爲8個字節,bMagicNumber爲0xFE;bVer爲版本01;nLength爲包長,包括包頭和包體的長度;nCmdId爲命令編碼,如登錄受權,初始化,發送數據,push推送等等;nSeq爲序列號,PushReq包的序列號固定爲0,其餘請求和回覆包的序列號務必保持一致,每次請求後序列號加1.性能
以廠商服務器主動發數據給設備的PushReq包爲例來講明變長包體,其對應定長包頭的nCmdId爲ECI_push_recvData = 30001。包體包括如下三個部分:網站
1) Push包標識ui
2) 自定義數據,指的是業務層本身定義的數據格式造成的數據。
3) 數據類型,如廠商自定義的數據,仍是微信客戶端Html5的數據等
3、Protobuffer的語法表述
Protobuffer對push_recvData包的表述以下:
Message相似C語言的struct數據結構,是Protobuffer消息定義的關鍵字。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。
4、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算法進行編碼;在其餘類型時就是「長度+原始內容編碼」。
5、PushReq包分析
咱們經過微信提供的AirSyncDebugger2.1.0.apk來分析Protobuffer對數據包的序列化。該APP用於微信和藍牙外設的通訊調試,其封裝了微信藍牙的協議,通常先用該APP調通藍牙協議,再和後臺服務器聯調。假設咱們自定義的消息內容爲fe cf 00 01 00 0c 20 01 00 00 00 00,即對應第二個字段bytesData,該消息內容是上一篇文章中服務器控制亮燈所發的消息,後臺服務器和藍牙外設的消息協議是自定義的。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表明廠商自定義數據。
6、基於微信硬件公衆平臺的智能控制方案開發專欄介紹
接下來嵌入式企鵝圈會將陸續公開基於微信硬件公衆平臺的智能控制開發技術細節,大體內容包括:
1. 物聯網架構和場景分析(已發)
2. 基於微信硬件公衆平臺的智能控制開發流程(已發)
3. 雲服務器搭建和公衆號配置
4. 公衆號菜單設置
5. 微信消息傳遞過程和微信設備接入接口協議
6. 微信硬件平臺後臺服務開發
7. 微信藍牙協議和受權、綁定過程
8.Protocol buffer序列化及其在微信藍牙協議中的應用(已發)
9. 藍牙外設控制開發
…