一般来说产品在设计协议时,会制定一定的协议二进制格式,包含协议头和协议正文,协议头包含一些标识信息,协议正文部分则是protobuf、json等承载的业务参数序列化后的字节串。我们在发送消息时要进行正确的参数序列化,接收到消息时首先要处理粘包,进行分包再解析。
前m个字节标识长度len,一般为1 、2 、 4 、 8
消息格式:
len(m字节) = n | msg(n字节) |
---|
-- 序列化
-- m = 1, 使用 string.pack("s1", content)
-- m = 2, 使用 string.pack("s2", content)
-- m = 4, 使用 string.pack("s4", content)
-- m = 8, 使用 string.pack("s8", content) 或者 string.unpack("s", content)
local data = string.pack("s2", "hello world!")
-- 分包解析代码
-- m = 1, 使用 string.unpack("s1", data)
-- m = 2, 使用 string.unpack("s2", data)
-- m = 4, 使用 string.unpack("s4", data)
-- m = 8, 使用 string.unpack("s8", data) 或者 string.unpack("s", data)
local pos = 1
local function RpcUnpack(data) --接收到的消息可能存在粘包,必须进行分包处理
while true do
local ok, msg, pos = pcall(string.unpack, "s2", data, pos)
if ok then
--完成一个分包
else
break
end
end
data = data:sub(pos) --余留消息,下次接收的消息需要合并到后面,再分包
end
m个字节标识长度len,一般为2 、 4 、 8
消息格式:
未知标识x(a字节) | len(m字节) = n | 未知标识y(b字节) | msg(n字节) |
---|
-- 序列化
-- m = 1, 使用 string.pack("B", data)
-- m = 2, 使用 string.pack("H", data)
-- m = 4, 使用 string.pack("I", data)
-- m = 8, 使用 string.pack("I8", data)
local msg = "hello world!"
local len = string.pack("I", #msg)
local data = x + len + y + msg
-- m = 1, 使用 string.unpack("B", data)
-- m = 2, 使用 string.unpack("H", data)
-- m = 4, 使用 string.unpack("I", data)
-- m = 8, 使用 string.unpack("I8", data)
local function RpcUnpack(data)
local data_len = #data
local head_len = a + m + b
local pos = 1
while true do
if data_len < pos + head_len - 1 then
break --data not enough
end
local len, pos = string.unpack("I", data, pos + a)
pos = pos + b
if data_len < pos + len - 1 then
break --data not enough
end
local msg = data:sub(pos, len)
--完成一个分包
pos = pos + len
end
end
还有其他各种各样的协议格式,如http的明文协议,websocket中使用变长的字计数标识正文长度,并且对正文进行掩码处理等等,使用时应当根据自身项目的设计细节再进行代码编写。