add snax.gateserver snax.loginserver snax.msgserver

This commit is contained in:
Cloud Wu
2014-07-15 12:05:58 +08:00
parent 43719de101
commit dbf492b761
13 changed files with 562 additions and 268 deletions

View File

@@ -1,18 +1,5 @@
package.cpath = "luaclib/?.so"
--[[ Status code
200 OK
400 Bad Request (通常是登陆协议错误)
401 Unauthorized (通常是登陆服务器或游戏服务器验证错误)
403 Forbidden (通常是连接游戏服务器的 index 已经过期)
404 Not Found (通常是游戏服务器未获得登陆服务器的通知)
406 Not Acceptable (通常是登陆服务器转发游戏服务器拒绝登陆)
412 Precondition Failed (通常是遗漏了和游戏服务器前次通讯的请求)
]]
local socket = require "clientsocket"
local crypt = require "crypt"
@@ -72,16 +59,38 @@ local b = crypt.base64encode(etoken)
socket.writeline(fd, crypt.base64encode(etoken))
local result = readline()
print(result)
local code = tonumber(string.sub(result, 1, 3))
assert(code == 200)
socket.close(fd)
local subid = crypt.base64decode(string.sub(result, 5))
print("login ok, subid=", subid)
----- connect to game server
local session = 0
local function send_request(v)
session = session + 1
local s = string.char(bit32.extract(session,24,8), bit32.extract(session,16,8), bit32.extract(session,8,8), bit32.extract(session,0,8))
socket.send(fd , v..s)
return session, v
end
local function recv_response(v)
local content = v:sub(1,-6)
local ok = v:sub(-5,-5):byte()
local session = 0
for i=-4,-1 do
local c = v:byte(i)
session = session + bit32.lshift(c,(-1-i) * 8)
end
return ok ~=0 , session, content
end
local input = {}
local fd = assert(socket.connect("127.0.0.1", 8888))
local function readpackage()
local line = table.remove(input, 1)
@@ -106,20 +115,32 @@ local function readpackage()
end
end
local index = 0
local request = 0
local handshake = string.format("%s#%s@%s:%d:%d", crypt.base64encode(token.user), crypt.base64encode(subid),crypt.base64encode(token.server) , index, request)
local hmac = crypt.hmac64(crypt.hashkey(handshake), secret)
socket.send(fd, handshake .. ":" .. crypt.base64encode(hmac))
print(readpackage())
socket.send(fd , "echo")
print(readpackage())
local index = 1
local function echo(text)
print("connect")
local fd = assert(socket.connect("127.0.0.1", 8888))
input = {}
local handshake = string.format("%s@%s#%s:%d", crypt.base64encode(token.user), crypt.base64encode(token.server),crypt.base64encode(subid) , index)
local hmac = crypt.hmac64(crypt.hashkey(handshake), secret)
socket.send(fd, handshake .. ":" .. crypt.base64encode(hmac))
print(readpackage())
print("===>",send_request(text))
print("===>",send_request(text .. " again"))
print("<===",recv_response(readpackage()))
print("<===",recv_response(readpackage()))
print("disconnect")
socket.close(fd)
end
echo "hello"
index = index + 1
echo "world"

88
examples/login/gated.lua Normal file
View File

@@ -0,0 +1,88 @@
local msgserver = require "snax.msgserver"
local crypt = require "crypt"
local skynet = require "skynet"
local loginservice = tonumber(...)
local server = {}
local users = {}
local username_map = {}
local internal_id = 0
-- login server disallow multi login, so login_handler never be reentry
-- call by login server
function server.login_handler(uid, secret)
if users[uid] then
error(string.format("%s is already login", uid))
end
internal_id = internal_id + 1
local username = msgserver.username(uid, internal_id, servername)
-- you can use a pool to alloc new agent
local agent = skynet.newservice "msgagent"
local u = {
username = username,
agent = agent,
uid = uid,
subid = internal_id,
}
-- trash subid (no used)
skynet.call(agent, "lua", "login", uid, internal_id, secret)
users[uid] = u
username_map[username] = u
msgserver.login(username, secret)
-- you should return unique subid
return internal_id
end
-- call by agent
function server.logout_handler(uid, subid)
local u = users[uid]
if u then
local username = msgserver.username(uid, subid, servername)
assert(u.username == username)
msgserver.logout(u.username)
users[uid] = nil
username_map[u.username] = nil
skynet.call(loginservice, "lua", "logout",uid, subid)
end
end
-- call by login server
function server.kick_handler(uid, subid)
local u = users[uid]
if u then
local username = msgserver.username(uid, subid, servername)
assert(u.username == username)
-- NOTICE: logout may call skynet.exit, so you should use pcall.
pcall(skynet.call, u.agent, "lua", "logout")
end
end
-- call by self (when socket disconnect)
function server.disconnect_handler(username)
local u = username_map[username]
if u then
skynet.call(u.agent, "lua", "afk")
end
end
-- call by self (when recv a request from client)
function server.request_handler(username, msg, sz)
local u = username_map[username]
return skynet.tostring(skynet.rawcall(u.agent, "client", msg, sz))
end
-- call by self (when gate open)
function server.register_handler(name)
servername = name
skynet.call(loginservice, "lua", "register_gate", servername, skynet.self())
end
msgserver.start(server)

View File

@@ -1,26 +1,17 @@
local login = require "gamefw.loginserver"
local login = require "snax.loginserver"
local crypt = require "crypt"
local skynet = require "skynet"
local server = {
host = "127.0.0.1",
port = 8001,
multilogin = false, -- disallow multilogin
name = "login_master",
}
local server_list = {}
local user_online = {}
local server_mt = {}
server_mt.__index = server_mt
function server_mt:kick(uid)
return skynet.call(self.address, "lua", "kick", self.name, uid)
end
function server_mt:login(uid, secret)
return skynet.call(self.address, "lua", "login", self.name, uid, secret)
end
local user_login = {}
function server.auth_handler(token)
-- the token is base64(user)@base64(server):base64(password)
@@ -34,27 +25,31 @@ end
function server.login_handler(server, uid, secret)
print(string.format("%s@%s is login, secret is %s", uid, server, crypt.hexencode(secret)))
local u = user_online[uid]
if u then
u:kick(uid)
end
assert(user_online[uid] == nil, "kick failed")
local gameserver = assert(server_list[server], "Unknown server")
local subid = gameserver:login(uid, secret)
user_online[uid] = gameserver
return tostring(subid)
-- only one can login, because disallow multilogin
local last = user_online[uid]
if last then
skynet.call(last.address, "lua", "kick", uid, last.subid)
end
if user_online[uid] then
error(string.format("user %s is already online", uid))
end
local subid = tostring(skynet.call(gameserver, "lua", "login", uid, secret))
user_online[uid] = { address = gameserver, subid = subid , server = server}
return subid
end
local CMD = {}
function CMD.register_gate(server, address)
server_list[server] = setmetatable( { name = server, address = address }, server_mt )
server_list[server] = address
end
function CMD.logout(uid)
function CMD.logout(uid, subid)
local u = user_online[uid]
if u then
print(string.format("%s@%s is logout", uid, u.name))
print(string.format("%s@%s is logout", uid, u.server))
user_online[uid] = nil
end
end

View File

@@ -1,12 +1,12 @@
local skynet = require "skynet"
skynet.start(function()
local loginserver = skynet.newservice "logind"
local gate = skynet.newservice "msggate"
local loginserver = skynet.newservice("logind")
local gate = skynet.newservice("gated", loginserver)
skynet.call(gate, "lua", "open" , {
port = 8888,
maxclient = 64,
loginserver = loginserver,
servername = "sample",
})
end)

View File

@@ -7,21 +7,46 @@ skynet.register_protocol {
}
local gate
local userid, subid
local CMD = {}
function CMD.init(source , uid, server)
function CMD.login(source, uid, sid, secret)
-- you may use secret to make a encrypted data stream
skynet.error(string.format("%s is login", uid))
gate = source
skynet.error(string.format("%s is coming", uid))
userid = uid
subid = sid
-- you may load user data from database
end
local function logout()
if gate then
skynet.call(gate, "lua", "logout", userid, subid)
end
skynet.exit()
end
function CMD.logout(source)
-- NOTICE: The logout MAY be reentry
skynet.error(string.format("%s is logout", userid))
logout()
end
function CMD.afk(source)
-- the connection is broken, but the user may back
skynet.error(string.format("AFK"))
end
skynet.start(function()
skynet.dispatch("lua", function(_, source, command, ...)
-- If you want to fork a work thread , you MUST do it in CMD.login
skynet.dispatch("lua", function(session, source, command, ...)
local f = assert(CMD[command])
skynet.ret(skynet.pack(f(source, ...)))
end)
skynet.dispatch("client", function(_,_, msg)
-- the simple ehco service
skynet.ret(msg)
end)
end)

View File

@@ -1,189 +0,0 @@
local skynet = require "skynet"
local gateserver = require "gamefw.gateserver"
local netpack = require "netpack"
local crypt = require "crypt"
local socketdriver = require "socketdriver"
local datacenter = require "datacenter"
local users = {}
skynet.register_protocol {
name = "client",
id = skynet.PTYPE_CLIENT,
}
local handler = {}
local handshake = {}
local connection = {}
local agent = {}
local login_master
-- launch an agent service to handle message from client
local function launch_agent(c)
local agent = assert(skynet.newservice "msgagent")
skynet.call(agent, "lua", "init", c.uid, c.server)
return agent
end
function handler.connect(fd, addr)
skynet.error(string.format("new connection from %s (fd=%d)", addr, fd))
handshake[fd] = true
gateserver.openclient(fd)
end
local function auth(fd, msg, sz)
-- base64(uid)@base64(server)#base64(subid):index:request:base64(hmac)
local message = netpack.tostring(msg, sz)
local username, index, request , hmac = string.match(message, "([^:]*):([^:]*):([^:]*):([^:]*)")
local content = users[username]
if content == nil then
return "404 Not Found"
end
local idx = assert(tonumber(index))
local req = assert(tonumber(request))
hmac = crypt.base64decode(hmac)
if idx < content.index then
return "403 Forbidden"
end
if req > content.request or req < content.reserved then
return "412 Precondition Failed"
end
local text = string.format("%s:%s:%s", username, index, request)
local v = crypt.hmac64(crypt.hashkey(text), content.secret)
if v ~= hmac then
return "401 Unauthorized"
end
content.index = idx
for i=content.reserved, request do
content.response[i] = nil
end
content.reserved = request
content.reqest = request
connection[fd] = content
if content.fd then
local last_fd = content.fd
connection[last_fd] = nil
gateserver.closeclient(last_fd)
end
content.fd = fd
if content.agent == nil then
content.agent = true
local ok, agent = pcall(launch_agent, content)
if ok then
content.agent = agent
else
content.agent = nil
skynet.error(string.format("Launch agent %s failed : %s", content.uid, agent))
connection[fd] = nil
gateserver.closeclient(fd)
end
end
end
function handler.message(fd, msg, sz)
if handshake[fd] then
local ok, result = pcall(auth, fd, msg, sz)
if not ok then
result = "400 Bad Request"
end
local close = result ~= nil
if result == nil then
result = "200 OK"
end
socketdriver.send(fd, netpack.pack(result))
if close then
gateserver.closeclient(fd)
end
handshake[fd] = nil
else
local c = connection[fd]
if c == nil or c.agent == nil then
local message = netpack.tostring(msg, sz)
skynet.error(string.format("Unknown fd = %d, message (%s) size = %d", fd, crypt.hexencode(message):sub(1,80), #message))
return
end
c.request = c.request + 1
local ret = c.response[c.request]
if ret == nil then
local ok, msg, sz = pcall(skynet.rawcall, c.agent, "client", msg, sz)
if ok then
ret = netpack.pack_string(msg, sz)
else
skynet.error(string.format("%s request error : %s", c.username, msg))
ret = netpack.pack_string ""
end
c.response[c.request] = ret
end
socketdriver.send(c.fd, ret)
end
end
function handler.close(fd)
handshake[fd] = nil
local c = connection[fd]
if c then
c.fd = nil
end
end
handler.error = handler.close
function handler.open(source, conf)
login_master = assert(conf.loginserver)
local servername = assert(conf.servername)
skynet.call(login_master, "lua", "register_gate", servername, skynet.self())
end
local CMD = {}
function CMD.login(source, server, uid, secret)
local subid = "1"
local username = crypt.base64encode(uid) .. '#'..crypt.base64encode(subid)..'@' .. crypt.base64encode(server)
users[username] = {
server = server,
uid = uid,
secret = secret,
index = 0,
request = 0,
reserved = 0,
response = {},
}
return subid
end
function CMD.logout(source)
local c = agent[source]
if c then
skynet.call(login_master, "lua", "logout", c.uid)
if c.fd then
gateserver.closeclient(c.fd)
end
end
end
function CMD.kick(source, server, uid)
local username = crypt.base64encode(uid) .. '@' .. crypt.base64encode(server)
local u = users[username]
if u and u.agent then
skynet.call(u.agent, "logout")
skynet.kill(u.agent)
u.agent = nil
end
end
function handler.command(cmd, source, ...)
local f = assert(CMD[cmd])
return f(source, ...)
end
gateserver.start(handler)