客户端使用lua协程的一个方案
· 阅读需 3 分钟
问题
客户端和服务器交互时通常采用的是注册回调
的方式,因为发送消息
和接受消息
的代码是分离的,客户端并不保存发送消息的上下文。如果客户端要做一个关联请求,就会因为缺少已经发送消息的状态,需要服务器辅助转发,这样即浪费网络流量,代码编写起来也不简洁。借助lua协程可以把异步回调转换成同步的书写方式,可以解决这一问题。下面是代码实现。
local co_create = coroutine.create
local co_running = coroutine.running
local co_yield = coroutine.yield
local co_resume = coroutine.resume
local function coresume(co, ...)
local ok, err = co_resume(co, ...)
if not ok then
error(debug.traceback(co, err))
end
return ok, err
end
local expect_queue = {}
local expect = {}
function expect.expect(cmd, if_fn)
table.insert(expect_queue, {cmd = cmd,fn = if_fn, co = co_running()})
return co_yield()
end
function expect.dispatch(cmd, msg)
---如果发生错误操作,终止所有等待
if cmd == "S2CErrorCode" then
for _, v in ipairs(expect_queue) do
local ok, err = xpcall(coresume, debug.traceback, v.co, false, msg)
if not ok then
print(err)
end
end
expect_queue = {}
return false
end
for _, v in ipairs(expect_queue) do
if v.cmd == cmd then
if v.fn then
msg = v.fn(msg)
end
if msg then
table.remove(expect_queue, _)
coresume(v.co, msg)
return true
end
end
end
return false
end
local co_num = 0
---for coroutine reused
local co_pool = setmetatable({}, {__mode = "kv"})
local function routine(fn)
local co = co_running()
while true do
co_num = co_num + 1
fn()
co_num = co_num - 1
co_pool[#co_pool + 1] = co
fn = co_yield()
end
end
function expect.async(fn)
local co = table.remove(co_pool)
if not co then
co = co_create(routine)
end
local _, res = coresume(co, fn)
if res then
return res
end
return co
end
return expect
使用,以一个客户端请求玩家初始化数据为例:
local expect = require("expect")
---需要在你的消息处理的地方调用先这个函数
---expect.dispatch
---返回false,继续执行旧的消息处理,返回true直接return
do
---点击某个按钮 或者 初始化时获取玩家数据
expect.async(function()
local sendmsg1 = {a=1,b=2}
--send(sendmsg1) --发送消息
---等待收第1条消息
print("receive", expect.expect("S2CPlayerData1"))
--这里可以使用发送消息状态数据
print(sendmsg1.a)
---等待收第2条消息
print("receive", expect.expect("S2CPlayerData3"))
---等待收第3条消息
print("receive", expect.expect("S2CPlayerData4"))
---等待收第5条消息
print("receive", expect.expect("S2CPlayerData5"))
---上下文状态一直存在 这里依然可以访问 sendmsg1
end)
end