更小、更快、更簡單Google ProtoBuf 跨語言通訊協議

maweiliang發表於2019-03-04

背景:

這幾天在寫一個服務端的socket的通訊伺服器,之前是自己定義的協議因為,因為只是android客戶端和伺服器通訊,後來iOS也連線到這個socket伺服器上,為了跨語言通訊,發現Google的 ProtoBuf(Protocol Buffer)支援跨語言通訊,而且效率非常高。

ProtoBuf是什麼?

Google Protocol Buffer( 簡稱 Protobuf) 是 Google 公司內部的混合語言資料標準,後來才開源的,它是一個靈活、高效、結構化的序列化資料結構,它與傳統的XML等通訊相比,它的更小、更快、更簡單。

protoBuf的優點

1.protoBuf在Google內部長期使用,產品穩定成熟,很多商業的專案都選擇使用
2.跨語言,它支援Java、C++、Python、ObJect-c、C#、Go等語言,
3.protoBuf編碼後訊息更小、有利於儲存傳輸
4.編碼和解碼的效率非常之高
5.支援不同版本的協議向前相容
6.支援自定義可選和必選欄位

更小、更快、更簡單Google ProtoBuf 跨語言通訊協議
圖片來自網路

更小、更快、更簡單Google ProtoBuf 跨語言通訊協議
圖片來自網路
protoBuf環境搭建(基於Windows)
我們先要下載ProtoBuf,,下載地址為github.com/google/prot… 這裡我是使用Windows版
更小、更快、更簡單Google ProtoBuf 跨語言通訊協議
下載完之後我們解壓
更小、更快、更簡單Google ProtoBuf 跨語言通訊協議

我們要是的是protoc.exe工具,它是根據.proto檔案生成對應的程式碼轉換工具,就這麼簡單ProtoBuf的編譯環境我們已經搞定了。

protoBuf的Demo

這裡使用Java語言實現,我們首選要定義一個protoBuf格式的通訊協議,Login.proto檔案

package test;
option java_package = "sg.com.protobuf";
option java_outer_classname = "LoginProto";
message Login{
  required int32 id = 1;
  required string name = 2;
  required string pws = 3;
  optional string email = 4;
}複製程式碼

package:指定生成Java程式碼檔案的包名
java_package:指定生Java類的包名
java_outer_classname:指定生成Java程式碼的外部類名稱。如果沒有指定該選項,Java程式碼的外部類名稱為當前檔案的檔名部分,同時還要將檔名轉換為駝峰格式,如:my_project.proto
message:protoBuf訊息定義的關鍵字,相當於Java中的class
required:資料型別的字首,表示該欄位為必要欄位,既在序列化和反序列化之前該欄位必須已經被賦值
repeated:表示這個欄位的值可以允許被重複多次,如果轉換成JAVA程式碼,此filed資料結構為list,有序的。可以在“repeated”型別的filed後使用“packed”--壓縮,提高資料傳輸的效率。
optional: 表示這個值是可選的允許為null
ProtoBuf型別和Java型別對應關係

更小、更快、更簡單Google ProtoBuf 跨語言通訊協議

ProtoBuf支援列舉型別,protobuf中enum型別的每個值是一個int32的數字,不像JAVA中那樣enum可以定義的非常複雜。如果enum中有些值是相同的,可以將“allow_alias”設定為true

完成訊息的定義之後,就可以通過protoc.exe編譯了

更小、更快、更簡單Google ProtoBuf 跨語言通訊協議

編譯的命令格式
protoc.exe -I=proto的輸入目錄 --java_out=java類輸出目錄 proto的輸入目錄包括包括proto檔案.
編譯好java類後,我們就可以例項化和序列化了

 LoginProto.Login.Builder builder = LoginProto.Login.newBuilder();
        builder.setId(1);
        builder.setName("sgtest");
        builder.setPws("123");
        builder.setEmail("test@163.com");
        //獲取login的例項
        LoginProto.Login login = builder.build();
        System.out.println(login);
        //序列化
        System.out.println("---------");
        byte[] bytes = login.toByteArray();
        System.out.println("leng:"+bytes.length);
        login =LoginProto.Login.parseFrom(bytes);
        System.out.println(login);複製程式碼

例項化一個物件是非常方便的,使用Google給我們生成的靜態方法就可以,通過toByteArray()方法來實現序列,parseFrom(bytes)方法來實現反序列化,你會看到protoBuf序列化後的位元組長度非常小,這個還不算強大的,下面再來看一中序列化的方式:

       //第二種序列化
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        login.writeDelimitedTo(byteArrayOutputStream);
      //反序列化
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        login = LoginProto.Login.parseDelimitedFrom(byteArrayInputStream);
        System.out.println(login.getEmail());複製程式碼

做過大量socket資料傳輸的同學都知道,socket很容易發生粘包、拆包的問題,會導致資料解析錯誤,protoBuf可以搞定它,login.writeDelimitedTo(byteArrayOutputStream)方法,將一個物件序列化輸出到一個位元組陣列流中,它在序列化的位元組陣列之前,新增一個varint32的數字表示位元組陣列的長度,所以實際放在位元組資料中內容應該是資料的長度加上資料的內容,下面是該方法的原始碼

更小、更快、更簡單Google ProtoBuf 跨語言通訊協議

在解碼通過先讀取varint,再讀取此長度的位元組;這種方式有效的解決了socket傳輸時粘包、拆包的問題。
最後給出demo下載地址:download.csdn.net/detail/mtx_…

相關文章