skynet 提供了一个通用的登陆服务器模版 snax.loginserver 。
先做如下定义:
当 C 试图登陆 G1 时,它进行下列流程:
以上流程,任何一个步骤失败,都会中断登陆流程,用户 C 会收到错误码。
登陆点会按照业务的需要,在确认用户登出后,通知 L 。登出可能发生在连接断开(基于长连接的应用)、用户主动退出、一定时间内没有收到用户消息等。
对于同一个用户登陆时,该用户已经在系统中时,通常有三种应对策略:
LoginServer 并不干涉你用哪种策略,可以自由定制。但对于后两种策略,给于一定的支持,简化业务逻辑实现的复杂性。
lualib/snax/loginserver.lua 是一个辅助库,帮助你实现登陆模块。
local login = require "snax.loginserver"
local server = {
host = "127.0.0.1",
port = 8001,
multilogin = false, -- disallow multilogin
name = "login_master",
-- config, etc
}
login(server)
取到 snax.loginserver 模块后,构造配置表,然后调用它就可以启动一个登陆服务器。
.login_master
这个名字。同时,你还需要注册一系列业务相关的方法。
function server.auth_handler(token)
你需要实现这个方法,对一个客户端发送过来的 token (step 2)做验证。如果验证不能通过,可以通过 error 抛出异常。如果验证通过,需要返回用户希望进入的登陆点(登陆点可以是包含在 token 内由用户自行决定,也可以在这里实现一个负载均衡器来选择);以及用户名。
在这个方法内做远程调用(skynet.call)是安全的。
function server.login_handler(server, uid, secret)
你需要实现这个方法,处理当用户已经验证通过后,该如何通知具体的登陆点(server )。框架会交给你用户名(uid)和已经安全交换到的通讯密钥。你需要把它们交给登陆点,并得到确认(等待登陆点准备好后)才可以返回。
如果关闭了 multilogin ,那么对于同一个 uid ,框架不会同时调用多次 login_handler
。在执行这个函数的过程中,如果用户发起了新的请求,他将直接收到拒绝的返回码。
如果打开 multilogin ,那么 login_handler
有可能并并行执行。由于这个函数在实现时,通常需要调用 skynet.call 让出控制权。所以请小心维护状态。例如,你希望在这个函数中将上一个实例踢下线。那么你需要在踢人操作后再次确认用户是否真的不在线(很有可能另一个登陆的竞争者恰好在此时又登陆成功了)。
一般你还希望这个登陆服务器可以接受一些 skynet 内部控制指令,比如让登陆点可以通知玩家下线了,动态注册新的登陆点等等操作。所以你可以定义这个函数来接收 skynet 内部传递过来的 lua 协议的消息:
function server.command_handler(command, source, ...)
command 是第一个参数,通常约定为指令类型。source 是发送来源地址,通常可以忽略掉。这个函数的返回值会作为回应返回给请求方。
你可以把登陆服务器做为一个单独的 skynet 进程使用,并用 cluster 模块和其它 skynet 进程做集群间通讯;也可以启动在一个 skynet 节点中。在附带的例子 examples/login/logind.lua 中,使用的后一种形式。
你可以参考 examples/login/client.lua 来实现配套的客户端。
登陆服务器和客户端的交互协议基于文本。每个请求和回应包,都以换行符 \n 分割。用户名、服务器名、token 等,为了保证可以正确在文本协议中传输,全部经过了 base64 编码。所以这些业务相关的串可以包含任何字符。
下列通讯流程的协议描述中,S2C 表示这是一个服务器向客户端发送的包;C2S 表示是一个客户端向服务器发送的包。
auth_handler
不认可 token login_handler
执行失败