Sproto是一个用C编写的高效序列化库,主要是想用来做Lua绑定。类似Google的 protocol buffers,但是速度更快。其设计得非常简单。只支持Lua支持的几种数据类型,其可以很容易的绑定到其他动态语言,或者直接在C中使用。 简介 其项目开源到 github.com/cloudwu/sproto
其主要包含一些提供给 Lua 使用的 API,一个语法解析模块(parser) sprotoparser,还有一个RPC API,加上 C库。
解析器 local parser = require "sprotoparser" parser.parse 把一个sproto 协议框架解析为一个二进制字符串 在解析的时候需要用到这个。可以用它来产生二进制字符串。框架文本和解析器在程序运行的时候并不需要
Lua API 我们先看看看它提供给 Lua 使用的API。
local sproto = require "sproto" local sprotocore = require "sproto.core" -- optional sproto.parse(schema) 通过一个 文本字符串 的框架生成一个 sproto 对象。 sproto.new(spbin) 通过一个 二进制的字符串(parser生成) 生成一个 sproto 对象。 sprotocore.newproto(spbin) 通过一个 二进制的字符串(parser生成) 生成一个 C sproto 对象。 sproto.sharenew(spbin) 从一个 sproto C 对象(sprotocore.newproto()生成)共享一个 sproto 对象。 sproto:exist_type(typename) 检查sproto对象中是否存在此类型。 sproto:encode(typename, luatable) 把一个Lua表以 typename 编码到二进制字符串内。 sproto:decode(typename, blob [,sz]) 以typename来解码一个 sproto:encode()产生的二进制字符串。如果 blob 是一个 lightuserdata (C 指针),sz 是必须的。 sproto:pencode(typename, luatable) 类似sproto:encode,但是会压缩结果。 sproto:pdecode(typename, blob [,sz]) 类似 sproto.decode,但是会先解压缩对象。 sproto:default(typename, type) 以类型名的默认值来建立一个表。类型可以是 nil, REQUEST, RESPONSE。 RPC API 这些API是对 core API的封装。
sproto:host([packagename]) 以 packagename 建立一个宿主对象 host 来传输RPC消息。
host:dispatch(blob [,sz]) 以 host 对象内的(packagename)来解压并解码(sproto:pdecode)二进制字符串。
如果 .type 存在,这是一个 有.type REQUEST 消息,返回REQUEST, protoname, message, responser, .ud。responser是一个用来编码 响应消息的函数。 当.session不存在时,responser将会是 nil。
如果 .type 不存在,这是一个给 .session 的 RESPONSE消息。返回 REPONSE, .session, message, .ud。
host:attach(sprotoobj) 建立一个以 sprotoobj 来压缩和编码请求消息的函数 function(protoname, message, session, ud)。
如果不想使用主机对象,可以用下面的API来编码和解码RPC消息。
sproto:request_encode(protoname, tbl) 以protoname 来编码一个请求消息。
sproto:response_encode(protoname, tbl) 以protoname 来编码一个响应消息。
sproto:request_decode(protoname, blob [,sz]) 解码一个请求消息。
sproto:response_decode(protoname, blob [,sz] 解码一个响应消息
数据类型 string : string binary : binary string (字符串的子类型) integer : 整型,最大整型是有符号64位的。 可以是一个不动点的特定精度的数字。 boolean : true or false 在类型前面添加一个 * 来表示一个数组。
可以指定一个主索引,数组将会被编码成一个无序的 map。
用户定义的类型可以是任何非保留的名字,也支持嵌套类型。
没有双精度或者实数类型。作者认为,这些类型非常少使用。如果果真需要的话,可以用字符串来序列化双精度数。如果需要十进制数,可以指定固定的精度。
枚举类型并不十分实用。我们在Lua定义一个 enum 表来实现。
协议定义 sproto是一个协议封装库。所以我们要定义我们自己的协议格式(schema)。
sproto消息是强类型的,而且不是自描述的。所以必须用一个特殊的语言来定义我们自己的消息结构。
然后调用 sprotoparser 来把 协议格式 解析为二进制字符串,这样 sproto 库就可以使用它。
可以离线解析,然后保存这些字符串,或者可以在程序运行的时候解析。
一个协议框架可能会像这样:
注释
.Person { # . 表示一个用户定义数据类型 name 0 : string # 内建数据类型 string id 1 : integer email 2 : string
.PhoneNumber { # 可以嵌套用户自定义数据类型
number 0 : string
type 1 : integer
}
phone 3 : *PhoneNumber # *PhoneNumber 表示数组
height 4 : integer(2) # (2) means a 1/100 精度的数 data 5 : binary # 二进制数据
}
.AddressBook { person 0 : *Person(id) # (id) 可选, Person.id 是一个主索引}
foobar 1 { # 定义一个新协议 (for RPC used) tag 1 request Person # 把数据类型 Person与 foobar.request 相关联 response { # 定义 foobar.response 的数据类型 ok 0 : boolean } } 一个框架可以是 被 sproto 框架语言自描述的:
.type { .field { name 0 : string buildin 1 : integer type 2 : integer # type is fixed-point number precision when buildin is SPROTO_TINTEGER; When buildin is SPROTO_TSTRING, it means binary string when type is 1. tag 3 : integer array 4 : boolean key 5 : integer # If key exists, array must be true, and it's a map. } name 0 : string fields 1 : *field }
.protocol { name 0 : string tag 1 : integer request 2 : integer # index response 3 : integer # index confirm 4 : boolean # response nil where confirm == true }
.group { type 0 : *type protocol 1 : *protocol } Wire protocol 每个整数以小端(little endian)格式序列化。
sproto消息必须是一个用户定义类型结构,每个结构编码成三个部分。h