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 = "xiaoming@163.com",
        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 = "xiaoming@163.com",
        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=xiaoming@163.com
[: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可能包含任意顺序的字节数据

发布了101 篇原创文章 · 获赞 113 · 访问量 20万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览