skynet框架應用 (十七) protobuffer

17 protobuffer

​ 假如我們要建立的skynet服務器與客戶端的連接方式爲長連接,且選擇了Google的Protobuf來定製我們的網絡協議,那麼,接下來我們要解決的問題就是:如何在skynet框架中使用socket+protobuf

​ 由於protobuf的lua版本的支持存在着部分缺陷,爲了避免踩坑,這裏我們直接使用雲風博客中推薦的pbc動態proto解析庫。

17.1 安裝PBC

1、下載pbc:跟下載skynet源碼一樣,通過gitpbc的源碼克隆到本地:


$ cd skynet/3rd/
$ git clone https://github.com/cloudwu/pbc.git

2、編譯安裝:


$ cd pbc
$ make

3、注意如果報如下錯,表示protobuf未安裝,沒有報錯就跳到第6步


make: protoc:命令未找到
Makefile:79: recipe for target 'build/addressbook.pb' failed
make: *** [build/addressbook.pb] Error 127

4、安裝protobuf


$ sudo apt-get install protobuf-c-compiler protobuf-compiler
$ protoc --version

5、再次編譯


$ make

6、工具編譯


$ cd ./binding/lua53
$ sudo make

7、如果報錯如下,沒有就跳到第9步


$ make
gcc -O2 -fPIC -Wall -shared -o protobuf.so -I../.. -I/usr/local/include -L../../build pbc-lua.c -lpbc
pbc-lua.c:4:17: fatal error: lua.h: 沒有那個文件或目錄
compilation terminated.
Makefile:11: recipe for target 'protobuf.so' failed
make: *** [protobuf.so] Error 1
$ 

8、上面的錯誤是因爲沒有安裝 lua5.3 skynet本身已經有lua5.3,只不過路勁沒指定,修改makefile如下


  CC = gcc
  CFLAGS = -O2 -fPIC -Wall
  LUADIR = ../../../lua   #這個路勁就是skynet/3rd/lua
  TARGET = protobuf.so
  
  .PHONY : all clean
  
  all : $(TARGET)
  
  $(TARGET) : pbc-lua53.c
      $(CC) $(CFLAGS) -shared -o $@ -I../.. -I$(LUADIR) -L../../build $^ -lpbc
  
  clean :
      rm -f $(TARGET)

9、編譯成功的話,將protobuf.so放在config文件中lua_cpath項配置的目錄下面,同時將protobuf.lua放在config文件lua_path配置的目錄下,就可以調用protobuf中的庫方法


$ cp protobuf.so ../../../../luaclib/
$ cp protobuf.lua ../../../../lualib/

17.2 生成protobuffer文件

1、先在項目根目錄下創建一個protos文件夾,用來存放協議文件, 比如創建一個Person.proto協議文件,內容如下:


$ cd skynet
$ mkdir protos
$ cd protos
$ vi test.proto #你也可以取他名字myname.proto

test.proto



package cs;  //定義包名
message test { //定義消息結構
  required string name = 1;   //name爲string類型,並且是必須的,1表示第一個字段
  required int32 age = 2;     //age爲int32類型,並且是必須的
  optional string email = 3;  //email爲string類型,並且是可選的
  required bool online = 4;    //online爲bool類型,並且是必須的
  required double account = 5; //account爲doubleg類型,並且是必須的
}

​ required 修飾的字段如果沒有指定值,將採用默認值填充;

​ optional修飾的字段如果沒有指定值,直接爲空;

2、將協議文件解析成.pb格式:


$ protoc --descriptor_set_out=test.pb test.proto

17.3 使用protobuffer文件


local protobuf = require "protobuf" --引入文件protobuf.lua
--註冊protobuffer文件
protobuf.register_fileprotofile

--根據註冊的protofile中的類定義進行序列化,返回得到一個stringbuffer
protobuf.encode("package.message", { ... })

--根據註冊的protofile中的類定義進行反序列化
protobuf.decode("package.message", stringbuffer)

示例代碼:testpbc.lua


local skynet = require "skynet"
local protobuf = require "protobuf" --引入文件protobuf.lua

skynet.start(function()
    protobuf.register_file "./protos/test.pb"
    skynet.error("protobuf register:test.pb")

    stringbuffer = protobuf.encode("cs.test", --對應person.proto協議的包名與類名
    {
        name = "xiaoming",
        age = 1,
        --email = "[email protected]",
        online = true,
        account = 888.88,
    })

    local data = protobuf.decode("cs.test",stringbuffer)
    skynet.error("------decode------ \nname=",data.name
        , ",\nage=", data.age
        , ",\nemail=", data.email)
        , ",\nonline=", data.online
        , ",\naccount=", data.account)
end)

運行結果:


$ ./skynet examples/config
testpbc
[:01000010] LAUNCH snlua testpbc
[:01000010] protobuf register:test.pb
[:01000010] ------decode------ 
name= xiaoming ,
age= 1 ,
email=  ,
online= true ,
account= 888.88

17.4 編寫一個稍微複雜點pbc服務

person.proto


package cs;
message Person {
  required string name = 1;   //Person第一個字段name爲string類型,並且是必須的
  required int32 id = 2;        
  optional string email = 3;  //Person第三個字段email爲string類型,並且是可選的

  enum PhoneType {   //定義一個枚舉類型
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {           //再定義一個消息類型PhoneNumber不是字段
    required string number = 1;   //PhoneNumber第一個字段number爲String類型,並且是必須的
    optional PhoneType type = 2 [default = HOME];  //第二個字段type爲PhoneTypeg類型,可選的
  }
  repeated PhoneNumber phone = 4;  //Person第四個字段phone爲PhoneNumber類型,是可重複的,相當於是數組
}

將協議文件解析成.pb格式:


$ protoc --descriptor_set_out=person.pb person.proto

示例代碼 testpbc.lua


local skynet = require "skynet"
local protobuf = require "protobuf" --引入文件protobuf.lua

skynet.start(function()
    protobuf.register_file "./protos/person.pb"
    skynet.error("protobuf register:person.pb")

    stringbuffer = protobuf.encode("cs.Person", --對應person.proto協議的包名與類名
    {
        name = "xiaoming",
        id = 1,
        email = "[email protected]",
        phone = {
                    {
                        number = "1388888888",
                        type = "MOBILE",
                    },
                    {
                        number = "8888888",
                    },
                    {
                        number = "87878787",
                        type = "WORK",
                    },

                }
    })


    local data = protobuf.decode("cs.Person",stringbuffer)
    skynet.error("decode name="..data.name..",id="..data.id..",email="..data.email)
    skynet.error("decode phone.type="..data.phone[1].type..",phone.number="..data.phone[1].number)
    skynet.error("decode phone.type="..data.phone[2].type..",phone.number="..data.phone[2].number)
    skynet.error("decode phone.type="..data.phone[3].type..",phone.number="..data.phone[3].number)
end)

運行結果:


$ ./skynet examples/conf
testpbc
[:01000010] LAUNCH snlua testpbc
[:01000010] protobuf register:person.pb
[:01000010] decode name=xiaoming,id=1,email=[email protected]
[:01000010] decode phone.type=MOBILE,phone.number=1388888888
[:01000010] decode phone.type=HOME,phone.number=8888888
[:01000010] decode phone.type=WORK,phone.number=87878787

17.5 protobuf數據類型

標量類型列表

proto類型C++類型備註
doubledouble
floatfloat
int32int32使用可變長編碼,編碼負數時不夠高效——如果字段可能含有負數,請使用sint32
int64int64使用可變長編碼,編碼負數時不夠高效——如果字段可能含有負數,請使用sint64
uint32uint32使用可變長編碼
uint64uint64使用可變長編碼
sint32int32使用可變長編碼,有符號的整型值,編碼時比通常的int32高效
sint64int64使用可變長編碼,有符號的整型值,編碼時比通常的int64高效
fixed32uint32總是4個字節,如果數值總是比228大的話,這個類型會比uint32高效
fixed64uint64總是8個字節,如果數值總是比256大的話,這個類型會比uint64高效
sfixed32int32總是4個字節
sfixed64int64總是8個字節
boolbool
stringstring一個字符串必須是UTF-8編碼或者7-bit ASCII編碼的文本
bytesstring可能包含任意順序的字節數據

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章