mirror of
https://github.com/cloudwu/skynet.git
synced 2026-07-24 12:20:41 +00:00
add snax.gateserver snax.loginserver snax.msgserver
This commit is contained in:
@@ -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
88
examples/login/gated.lua
Normal 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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user