目前在做的一个老项目,使用的网络协议是自有协议,序列化与反序列化都需要手写代码,而且是C++的。由于新设计了一个python服务器,需要解析相应的网络协议封包,决定不能再这么蛮干了(毕竟python),目标是相应的序列化与反序列化不再需要干预,只需要定义封包格式就好。
- 使用方式
先上实现后的使用方式:
- 定义结构
class Phone(BaseProtocol):
def __init__(self):
BaseProtocol.__init__(self)
self.AppendDef("number", FixedSizeString, 16)
self.AppendDef("left_money", UINT32)
class Person(Protocol):
def __init__(self):
Protocol.__init__(self)
self.AppendDef("uid", UINT32)
self.AppendDef("age", UINT16)
self.AppendDef("phone_count", UINT8)
self.AppendDef("phone", List, self, "phone_count", Phone)
不需要额外手写序列化代码
- 使用
p = Person()
p.uid = 1002
p.age = 11
p.phone_count = 2
p.phone[0].number.setStr("1111000")
p.phone[0].left_money = 33
p.phone[1].number.setStr("11112222")
p.phone[1].left_money = 34
print p.payload
print p.pack()
其中payload省略了封包头,pack函数就是序列化操作,输出结果:
Person.uid -> 1002
Person.age -> 11
Person.phone_count -> 2
Person.phone._0.number.len -> 7
Person.phone._0.number.val -> 1111000
Person.phone._0.left_money -> 33
Person.phone._1.number.len -> 8
Person.phone._1.number.val -> 11112222
Person.phone._1.left_money -> 34
HW����>�
111100011112222"
- 实现
网络协议反序列化比较需要处理的地方是可变长度的对象数组,其长度信息一般都会在其它字段中声明,也导致了同类型的每个封包长度不一
python作为一门特别灵活的语言,可以在需要的时候获取这个属性,所以定义的时候可以只指定需要访问的字段名:
self.AppendDef("phone", List, self, "phone_count", Phone)
为了实现类似于数组方式的存取玩法:
p.phone_count = 2
p.phone[0].number.setStr("1111000")
p.phone[0].left_money = 33
p.phone[1].number.setStr("11112222")
p.phone[1].left_money = 34
需要使用__getitem__以及__setitem__两个方法:
def checkcount(self):
if self.count != -1:
return
self.count = getattr(self.obj, self.key)
for i in range(self.count):
self.AppendDef('_%s' % i, self.cls, *self.args)
def __getitem__(self, idx):
self.checkcount()
return getattr(self, '_%s' % idx)
def __setitem__(self, idx, value):
self.checkout()
setattr(self, '_%s' % idx, value)
在protobuf、json等等序列化协议盛行的时代,自有协议的设计貌似没有太大的价值,但是设计并理解一套协议对于自我提升很有帮助,同时提高了协议的安全性,也对于维护或者对接一些老系统而不得不使用自有协议时也是必要的。