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

背景:

這幾天在寫一個服務端的socket的通信服務器,以前是本身定義的協議由於,由於只是android客戶端和服務器通信,後來iOS也鏈接到這個socket服務器上,爲了跨語言通信,發現Google的 ProtoBuf(Protocol Buffer)支持跨語言通信,並且效率很是高。javascript

ProtoBuf是什麼?

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

protoBuf的優勢

1.protoBuf在Google內部長期使用,產品穩定成熟,不少商業的項目都選擇使用
2.跨語言,它支持Java、C++、Python、ObJect-c、C#、Go等語言,
3.protoBuf編碼後消息更小、有利於存儲傳輸
4.編碼和解碼的效率很是之高
5.支持不一樣版本的協議向前兼容
6.支持自定義可選和必選字段
android

圖片來自網絡

圖片來自網絡
protoBuf環境搭建(基於Windows)
咱們先要下載ProtoBuf,,下載地址爲 github.com/google/prot… 這裏我是使用Windows版
下載完以後咱們解壓

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

protoBuf的Demo

這裏使用Java語言實現,咱們首選要定義一個protoBuf格式的通信協議,Login.proto文件git

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類型對應關係
github


ProtoBuf支持枚舉類型,protobuf中enum類型的每一個值是一個int32的數字,不像JAVA中那樣enum能夠定義的很是複雜。若是enum中有些值是相同的,能夠將「allow_alias」設定爲true

完成消息的定義以後,就能夠經過protoc.exe編譯了
數組


編譯的命令格式
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的數字表示字節數組的長度,因此實際放在字節數據中內容應該是數據的長度加上數據的內容,下面是該方法的源碼
網絡


在解碼經過先讀取varint,再讀取此長度的字節;這種方式有效的解決了socket傳輸時粘包、拆包的問題。
最後給出demo下載地址: download.csdn.net/detail/mtx_…
相關文章
相關標籤/搜索