刚好我今早还想到 要自己做一个轮子,
因为 protobuffer 岁好,但是 在erlang中使用起来确实麻烦,
如果自己定义 消息的二进制结构,那么每个消息服务端和客户端都要自己去 pack/unpack,
比protobuf这种麻烦多了,
于是便有了这个想法:
用python写protocol定义, 然后生成对应语言的操作代码
生成的代码直接去 pack/unpack 二进制数据。
这样 protocol 定义写起来非常方便,而且自动生成各种语言的代码,
方便,省事,而且为了简单轻量:
可能有以下特性:
1. 不做错误检查, pack进去的数据由 人保证不写错, 否则在 serialize 的可能会报错
2. 生成的代码,没有压缩算法,一个 32位整数,哪怕你之填充了一个1, 那么也是老老实实的占用4个字节。
3. 保持简单,生成的二进制结构就是这么的stupid:
Binary:
[4 bytes length][ * body *]
Body:
[4 bytes protocol id][ * data *]
Data:
[1 byte date type sign number][ * value * ]...
[1 byte date type sign number][4 bytes string length][* string value *]...
[1 byte date type sign number][4 bytes repeated amount][* repeated value *]...
Repated Value:
[1 byte date type sign number][* value *]...
比如举个例子:
协议定义
class Weapon(EmbeddedProtocol):
----id = IntegerField(bytes=4)
----attack = FloatField()
@
Spec(protocol_id=10) # 协议号
class Person(Protocol):
----id = IntegerField(bytes=8) # 64 bits integer
----gender = IntegerField(bytes=1) # 8 bits integer
----name = StringField()
----position = PointField()
----weapons = ListField(Weapon)
然后用python直接生产对应语言的 代码。
python例子(以及所有支持OOP的语言),生成后的代码使用起来类似这样:
p = Person()
p.id = 1
p.gender = 1
p.name = "My Name"
p.position = (23, 56)
p.weapons.extend([
----Weapon(id=11, attack=99),
----Weapon(id=12, attack=101),
])
binary = p.Serialize()
erlang 例子,生成后的代码使用类似这样:
P = person:new(),
P1 = person:set(P, id, 1),
P2 = person:set(P1, gender, 1),
%% 或者一次设置多个Field
P3 = P2#person(name=<<"My Name">>, position=[23, 56]),
W1 = weapon:new(#weapon(id=11, attack=99)),
W2 = weapon:new(#weapon(id=12, attack=101)),
P4 = person:append_to_field(P3, weapons, [W1, W2])
Binary = person:serialize(P4)