一次序列化与反序列化自有协议的经历(lua版本)


之前写过一篇python序列化反序列化的文章(http://blog.csdn.net/q_yang1987/article/details/52194860),最近为这个协议实现了一个lua版本,用这个练手感觉对初学lua的我来说很有价值,本文记录一下这个库的实现细节:


  • 使用
使用时需要先定义Protocol协议结构:

local Proto = require("proto2")

local Phone = Proto.Protocol:new({
        {"number", Proto.String},
        {"money", Proto.UINT32},
    })

local Person = Proto.Protocol:new({
        --{"header", Proto.Header},
        {"uid", Proto.UINT32},
        {"age", Proto.UINT16},
        {"phone_count", Proto.UINT8},
        {"phone", Proto.List:new("phone_count", Phone)},
    })

Proto.Protocol:new出来的是protocol对象,使用的时候需要由Protocol对象生成对应的Message,比如下面的基本用法
local function test_basic()
    local xiaoxiao = Proto.Message:new(Person)
    xiaoxiao.uid = 111000000
    xiaoxiao.age = 22
    xiaoxiao.phone_count = 1
    local phone = Proto.Message:new(Phone)
    phone.number = "2211111"
    phone.money = 100
    xiaoxiao.phone[1] = phone
    print(xiaoxiao)
end

local function test_basic2()
    local Company = Proto.Protocol:new({
            {"header", Person},
            {"count", Proto.UINT32},
            {"employee", Proto.List:new("count", Person)},
        })
    titan = Proto.Message:new(Company)
    titan.header.uid = 1
    titan.header.age = 30
    titan.header.phone_count = 0
    titan.count = 2
    titan.employee[1] = titan.header
    xiaoming = Proto.Message:new(Person)
    xiaoming.uid = 2
    xiaoming.age = 31
    xiaoming.phone_count = 1
    xiaoming.phone[1] = "111222333"
    titan.employee[2] = xiaoming
    print(titan)
enduid=111000000
age=22
phone_count=1
phone=[
<1> number=2211111 money=100 
]

header.uid=1
header.age=30
header.phone_count=0
header.phone=[
]
count=2
employee=[
<1> uid=1 age=30 phone_count=0 phone=[ ] 
<2> uid=2 age=31 phone_count=1 phone=[ <1> 111222 ] 
]
print(message_object)也做了一些友好的输出

pack和unpack函数对应于序列化和反序列化函数,格式兼容之前介绍的python协议
local function test_copy()
    local copy = Proto.Message:new(Person)
    copy:unpack(xiaoxiao:pack())
    print("original", xiaoxiao)
    -- do some modify
    copy.phone_count = 2
    local phone2 = Proto.Message:new(Phone)
    phone2.number = "111111111"
    phone2.money = 100
    copy.phone[2] = phone2
    print(copy)
    
    copy2 = Proto.Message:new(Person)
    copy2:unpack(copy:pack())
    print(copy2)
endoriginal	uid=111000000
age=22
phone_count=1
phone=[
<1> number=2211111 money=100 
]

uid=111000000
age=22
phone_count=2
phone=[
<1> number=2211111 money=100 
<2> number=111111111 money=100 
]

uid=111000000
age=22
phone_count=2
phone=[
<1> number=2211111 money=100 
<2> number=111111111 money=100 
]

  • 关于lua中的int 64

lua在实现uint64时候有一个坑,lua中只有number类型,也就是double 64,在表示UINT64的时候存在精度丢失问题,可以通过下面的代码体现

a = 2000000001 * 2000000001

print(a)

b = 4.000000004e+18

print(a==b)

> true

也就是1丢失了,http://blog.codingnow.com/2012/04/lua_int64.html云风blog里面为了应对这个问题,写了一个库,本文在实现的时候也采用了这个库https://github.com/cloudwu/lua-int64,但在接口使用上遵照云风库的接口(比如从字节初始化,int64对象之间才能进行精确的加减乘除):

local function test_u64()
    local Test64 = Proto.Protocol:new({
            {"u64", Proto.UINT64},
        })
    local t64 = Proto.Message:new(Test64)
    raw = '\1' .. string.rep('\0', 6) .. '\0'
    t64.u64 = raw
    print(t64)
    t64.u64 = t64.u64 / 0x10000 * 0xF
    print(t64)
endu64=int64: 0x100000000000001

u64=int64: 0xF0000000000

实现的时候通过__index, __newindex元方法内部存储为int64,序列化以及反序列化也比较有意思:

unpack = function (self, bytes)
    return i64.new(string.reverse(bytes:sub(1, self.length())), 9
end

pack = function (self, value)
    high = value / 0x10000000
    low = value % 0x10000000
    local bytes = struct.pack(">I", #high)
    local bytes = bytes .. struct.pack(">I", #low)
    return bytes
end
思路就是将64位切成32位,按网络序传输,其中用到的序列化库struct是: http://www.inf.puc-rio.br/~roberto/struct/

做这个库最大的目的就是为了之后为实现lua服务器做网络底层支持,同时也作为学习lua过程的一个练手




你可能感兴趣的:(lua)