mirror of
https://github.com/cloudwu/skynet.git
synced 2026-07-24 20:23:06 +00:00
add snax.gateserver snax.loginserver snax.msgserver
This commit is contained in:
@@ -1,3 +1,8 @@
|
|||||||
|
Dev version
|
||||||
|
-----------
|
||||||
|
* skynet.exit will quit service immediately.
|
||||||
|
* Add snax.gateserver, snax.loginserver, snax.msgserver
|
||||||
|
|
||||||
v0.4.2 (2014-7-14)
|
v0.4.2 (2014-7-14)
|
||||||
-----------
|
-----------
|
||||||
* Bugfix : invalid negative socket id
|
* Bugfix : invalid negative socket id
|
||||||
|
|||||||
@@ -1,18 +1,5 @@
|
|||||||
package.cpath = "luaclib/?.so"
|
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 socket = require "clientsocket"
|
||||||
local crypt = require "crypt"
|
local crypt = require "crypt"
|
||||||
|
|
||||||
@@ -72,16 +59,38 @@ local b = crypt.base64encode(etoken)
|
|||||||
socket.writeline(fd, crypt.base64encode(etoken))
|
socket.writeline(fd, crypt.base64encode(etoken))
|
||||||
|
|
||||||
local result = readline()
|
local result = readline()
|
||||||
|
print(result)
|
||||||
local code = tonumber(string.sub(result, 1, 3))
|
local code = tonumber(string.sub(result, 1, 3))
|
||||||
assert(code == 200)
|
assert(code == 200)
|
||||||
socket.close(fd)
|
socket.close(fd)
|
||||||
|
|
||||||
local subid = crypt.base64decode(string.sub(result, 5))
|
local subid = crypt.base64decode(string.sub(result, 5))
|
||||||
|
|
||||||
|
print("login ok, subid=", subid)
|
||||||
|
|
||||||
----- connect to game server
|
----- 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 input = {}
|
||||||
local fd = assert(socket.connect("127.0.0.1", 8888))
|
|
||||||
|
|
||||||
local function readpackage()
|
local function readpackage()
|
||||||
local line = table.remove(input, 1)
|
local line = table.remove(input, 1)
|
||||||
@@ -106,20 +115,32 @@ local function readpackage()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local index = 0
|
local index = 1
|
||||||
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 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 crypt = require "crypt"
|
||||||
local skynet = require "skynet"
|
local skynet = require "skynet"
|
||||||
|
|
||||||
local server = {
|
local server = {
|
||||||
host = "127.0.0.1",
|
host = "127.0.0.1",
|
||||||
port = 8001,
|
port = 8001,
|
||||||
|
multilogin = false, -- disallow multilogin
|
||||||
name = "login_master",
|
name = "login_master",
|
||||||
}
|
}
|
||||||
|
|
||||||
local server_list = {}
|
local server_list = {}
|
||||||
local user_online = {}
|
local user_online = {}
|
||||||
|
local user_login = {}
|
||||||
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
|
|
||||||
|
|
||||||
function server.auth_handler(token)
|
function server.auth_handler(token)
|
||||||
-- the token is base64(user)@base64(server):base64(password)
|
-- the token is base64(user)@base64(server):base64(password)
|
||||||
@@ -34,27 +25,31 @@ end
|
|||||||
|
|
||||||
function server.login_handler(server, uid, secret)
|
function server.login_handler(server, uid, secret)
|
||||||
print(string.format("%s@%s is login, secret is %s", uid, server, crypt.hexencode(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 gameserver = assert(server_list[server], "Unknown server")
|
||||||
local subid = gameserver:login(uid, secret)
|
-- only one can login, because disallow multilogin
|
||||||
user_online[uid] = gameserver
|
local last = user_online[uid]
|
||||||
return tostring(subid)
|
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
|
end
|
||||||
|
|
||||||
local CMD = {}
|
local CMD = {}
|
||||||
|
|
||||||
function CMD.register_gate(server, address)
|
function CMD.register_gate(server, address)
|
||||||
server_list[server] = setmetatable( { name = server, address = address }, server_mt )
|
server_list[server] = address
|
||||||
end
|
end
|
||||||
|
|
||||||
function CMD.logout(uid)
|
function CMD.logout(uid, subid)
|
||||||
local u = user_online[uid]
|
local u = user_online[uid]
|
||||||
if u then
|
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
|
user_online[uid] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
local skynet = require "skynet"
|
local skynet = require "skynet"
|
||||||
|
|
||||||
skynet.start(function()
|
skynet.start(function()
|
||||||
local loginserver = skynet.newservice "logind"
|
local loginserver = skynet.newservice("logind")
|
||||||
local gate = skynet.newservice "msggate"
|
local gate = skynet.newservice("gated", loginserver)
|
||||||
|
|
||||||
skynet.call(gate, "lua", "open" , {
|
skynet.call(gate, "lua", "open" , {
|
||||||
port = 8888,
|
port = 8888,
|
||||||
maxclient = 64,
|
maxclient = 64,
|
||||||
loginserver = loginserver,
|
|
||||||
servername = "sample",
|
servername = "sample",
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
|||||||
@@ -7,21 +7,46 @@ skynet.register_protocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
local gate
|
local gate
|
||||||
|
local userid, subid
|
||||||
|
|
||||||
local CMD = {}
|
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
|
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
|
end
|
||||||
|
|
||||||
skynet.start(function()
|
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])
|
local f = assert(CMD[command])
|
||||||
skynet.ret(skynet.pack(f(source, ...)))
|
skynet.ret(skynet.pack(f(source, ...)))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
skynet.dispatch("client", function(_,_, msg)
|
skynet.dispatch("client", function(_,_, msg)
|
||||||
|
-- the simple ehco service
|
||||||
skynet.ret(msg)
|
skynet.ret(msg)
|
||||||
end)
|
end)
|
||||||
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)
|
|
||||||
@@ -457,9 +457,9 @@ lpack_padding(lua_State *L) {
|
|||||||
size_t content_sz;
|
size_t content_sz;
|
||||||
uint8_t *buffer;
|
uint8_t *buffer;
|
||||||
const char * ptr = tolstring(L, &content_sz, 2);
|
const char * ptr = tolstring(L, &content_sz, 2);
|
||||||
size_t header_sz = 0;
|
size_t cookie_sz = 0;
|
||||||
const char * header = luaL_checklstring(L,1,&header_sz);
|
const char * cookie = luaL_checklstring(L,1,&cookie_sz);
|
||||||
size_t len = header_sz + content_sz;
|
size_t len = cookie_sz + content_sz;
|
||||||
|
|
||||||
if (len > 0x10000) {
|
if (len > 0x10000) {
|
||||||
return luaL_error(L, "Invalid size (too long) of data : %d", (int)len);
|
return luaL_error(L, "Invalid size (too long) of data : %d", (int)len);
|
||||||
@@ -472,8 +472,8 @@ lpack_padding(lua_State *L) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
write_size(buffer, len);
|
write_size(buffer, len);
|
||||||
memcpy(buffer+2, header, header_sz);
|
memcpy(buffer+2, ptr, content_sz);
|
||||||
memcpy(buffer+2+header_sz, ptr, content_sz);
|
memcpy(buffer+2+content_sz, cookie, cookie_sz);
|
||||||
lua_pushlstring(L, (const char *)buffer, len+2);
|
lua_pushlstring(L, (const char *)buffer, len+2);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@@ -485,10 +485,21 @@ ltostring(lua_State *L) {
|
|||||||
int size = luaL_checkinteger(L, 2);
|
int size = luaL_checkinteger(L, 2);
|
||||||
if (ptr == NULL) {
|
if (ptr == NULL) {
|
||||||
lua_pushliteral(L, "");
|
lua_pushliteral(L, "");
|
||||||
|
} else {
|
||||||
|
if (lua_isnumber(L, 3)) {
|
||||||
|
int offset = lua_tointeger(L, 3);
|
||||||
|
if (offset < 0) {
|
||||||
|
return luaL_error(L, "Invalid offset %d", offset);
|
||||||
|
}
|
||||||
|
if (offset > size) {
|
||||||
|
offset = size;
|
||||||
|
}
|
||||||
|
lua_pushlstring(L, (const char *)ptr + offset, size-offset);
|
||||||
} else {
|
} else {
|
||||||
lua_pushlstring(L, (const char *)ptr, size);
|
lua_pushlstring(L, (const char *)ptr, size);
|
||||||
skynet_free(ptr);
|
skynet_free(ptr);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ function suspend(co, result, command, param, size)
|
|||||||
if not result then
|
if not result then
|
||||||
local session = session_coroutine_id[co]
|
local session = session_coroutine_id[co]
|
||||||
local addr = session_coroutine_address[co]
|
local addr = session_coroutine_address[co]
|
||||||
if session and session ~= 0 then
|
if session then
|
||||||
c.send(addr, skynet.PTYPE_ERROR, session, "")
|
c.send(addr, skynet.PTYPE_ERROR, session, "")
|
||||||
end
|
end
|
||||||
session_coroutine_id[co] = nil
|
session_coroutine_id[co] = nil
|
||||||
@@ -151,6 +151,9 @@ function suspend(co, result, command, param, size)
|
|||||||
-- coroutine exit
|
-- coroutine exit
|
||||||
session_coroutine_id[co] = nil
|
session_coroutine_id[co] = nil
|
||||||
session_coroutine_address[co] = nil
|
session_coroutine_address[co] = nil
|
||||||
|
elseif command == "QUIT" then
|
||||||
|
-- service exit
|
||||||
|
return
|
||||||
else
|
else
|
||||||
error("Unknown command : " .. command .. "\n" .. debug.traceback(co))
|
error("Unknown command : " .. command .. "\n" .. debug.traceback(co))
|
||||||
end
|
end
|
||||||
@@ -264,6 +267,8 @@ function skynet.exit()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
c.command("EXIT")
|
c.command("EXIT")
|
||||||
|
-- quit service
|
||||||
|
coroutine_yield "QUIT"
|
||||||
end
|
end
|
||||||
|
|
||||||
function skynet.kill(name)
|
function skynet.kill(name)
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ function gateserver.start(handler)
|
|||||||
socket = socketdriver.listen(address, port)
|
socket = socketdriver.listen(address, port)
|
||||||
socketdriver.start(socket)
|
socketdriver.start(socket)
|
||||||
if handler.open then
|
if handler.open then
|
||||||
handler.open(source, conf)
|
return handler.open(source, conf)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -88,8 +88,8 @@ function gateserver.start(handler)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function MSG.close(fd)
|
function MSG.close(fd)
|
||||||
if handler.close then
|
if handler.disconnect then
|
||||||
handler.close(fd)
|
handler.disconnect(fd)
|
||||||
end
|
end
|
||||||
close_fd(fd)
|
close_fd(fd)
|
||||||
end
|
end
|
||||||
@@ -1,6 +1,35 @@
|
|||||||
local skynet = require "skynet"
|
local skynet = require "skynet"
|
||||||
local socket = require "socket"
|
local socket = require "socket"
|
||||||
local crypt = require "crypt"
|
local crypt = require "crypt"
|
||||||
|
local table = table
|
||||||
|
local string = string
|
||||||
|
local assert = assert
|
||||||
|
|
||||||
|
--[[
|
||||||
|
|
||||||
|
Protocol:
|
||||||
|
|
||||||
|
line (\n) based text protocol
|
||||||
|
|
||||||
|
1. Server->Client : base64(8bytes random challenge)
|
||||||
|
2. Client->Server : base64(8bytes handshake client key)
|
||||||
|
3. Server: Gen a 8bytes handshake server key
|
||||||
|
4. Server->Client : base64(DH-Exchange(server key))
|
||||||
|
5. Server/Client secret := DH-Secret(client key/server key)
|
||||||
|
6. Client->Server : base64(HMAC(challenge, secret))
|
||||||
|
7. Client->Server : DES(secret, base64(token))
|
||||||
|
8. Server : call auth_handler(token) -> server, uid (A user defined method)
|
||||||
|
9. Server : call login_handler(server, uid, secret) (A user defined method)
|
||||||
|
|
||||||
|
Error Code:
|
||||||
|
400 Bad Request . challenge failed
|
||||||
|
401 Unauthorized . unauthorized by auth_handler
|
||||||
|
403 Forbidden . login_handler failed
|
||||||
|
406 Not Acceptable . already in login (disallow multi login)
|
||||||
|
|
||||||
|
Success:
|
||||||
|
200 base64(subid)
|
||||||
|
]]
|
||||||
|
|
||||||
local function write(fd, text)
|
local function write(fd, text)
|
||||||
assert(socket.write(fd, text), "socket error")
|
assert(socket.write(fd, text), "socket error")
|
||||||
@@ -51,6 +80,8 @@ local function launch_slave(auth_handler)
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local user_login = {}
|
||||||
|
|
||||||
local function accept(conf, s, fd, addr)
|
local function accept(conf, s, fd, addr)
|
||||||
-- call slave auth
|
-- call slave auth
|
||||||
local ok, server, uid, secret = skynet.call(s, "lua", fd, addr)
|
local ok, server, uid, secret = skynet.call(s, "lua", fd, addr)
|
||||||
@@ -61,12 +92,24 @@ local function accept(conf, s, fd, addr)
|
|||||||
error(server)
|
error(server)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if not conf.multilogin then
|
||||||
|
if user_login[uid] then
|
||||||
|
write(fd, "406 Not Acceptable\n")
|
||||||
|
error(string.format("User %s is already login", uid))
|
||||||
|
end
|
||||||
|
|
||||||
|
user_login[uid] = true
|
||||||
|
end
|
||||||
|
|
||||||
local ok, err = pcall(conf.login_handler, server, uid, secret)
|
local ok, err = pcall(conf.login_handler, server, uid, secret)
|
||||||
|
-- unlock login
|
||||||
|
user_login[uid] = nil
|
||||||
|
|
||||||
if ok then
|
if ok then
|
||||||
err = err or ""
|
err = err or ""
|
||||||
write(fd, "200 "..crypt.base64encode(err).."\n")
|
write(fd, "200 "..crypt.base64encode(err).."\n")
|
||||||
else
|
else
|
||||||
write(fd, "406 Not Acceptable\n")
|
write(fd, "403 Forbidden\n")
|
||||||
error(err)
|
error(err)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -92,8 +135,8 @@ local function launch_master(conf)
|
|||||||
skynet.newservice(SERVICE_NAME)
|
skynet.newservice(SERVICE_NAME)
|
||||||
end
|
end
|
||||||
|
|
||||||
local id = socket.listen(host, port)
|
|
||||||
skynet.error(string.format("login server listen at : %s %d", host, port))
|
skynet.error(string.format("login server listen at : %s %d", host, port))
|
||||||
|
local id = socket.listen(host, port)
|
||||||
socket.start(id , function(fd, addr)
|
socket.start(id , function(fd, addr)
|
||||||
local s = slave[balance]
|
local s = slave[balance]
|
||||||
balance = balance + 1
|
balance = balance + 1
|
||||||
299
lualib/snax/msgserver.lua
Normal file
299
lualib/snax/msgserver.lua
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
local skynet = require "skynet"
|
||||||
|
local gateserver = require "snax.gateserver"
|
||||||
|
local netpack = require "netpack"
|
||||||
|
local crypt = require "crypt"
|
||||||
|
local socketdriver = require "socketdriver"
|
||||||
|
local assert = assert
|
||||||
|
local b64encode = crypt.base64encode
|
||||||
|
local b64decode = crypt.base64decode
|
||||||
|
|
||||||
|
--[[
|
||||||
|
|
||||||
|
Protocol:
|
||||||
|
|
||||||
|
All the number type is big-endian
|
||||||
|
|
||||||
|
Shakehands (The first package)
|
||||||
|
|
||||||
|
Client -> Server :
|
||||||
|
|
||||||
|
base64(uid)@base64(server)#base64(subid):index:base64(hmac)
|
||||||
|
|
||||||
|
Server -> Client
|
||||||
|
|
||||||
|
XXX ErrorCode
|
||||||
|
404 User Not Found
|
||||||
|
403 Index Expired
|
||||||
|
401 Unauthorized
|
||||||
|
400 Bad Request
|
||||||
|
200 OK
|
||||||
|
|
||||||
|
Req-Resp
|
||||||
|
|
||||||
|
Client -> Server : Request
|
||||||
|
word size (Not include self)
|
||||||
|
string content (size-4)
|
||||||
|
dword session
|
||||||
|
|
||||||
|
Server -> Client : Response
|
||||||
|
word size (Not include self)
|
||||||
|
string content (size-5)
|
||||||
|
byte ok (1 is ok, 0 is error)
|
||||||
|
dword session
|
||||||
|
|
||||||
|
API:
|
||||||
|
server.userid(username)
|
||||||
|
return uid, subid, server
|
||||||
|
|
||||||
|
server.username(uid, subid, server)
|
||||||
|
return username
|
||||||
|
|
||||||
|
server.login(username, secret)
|
||||||
|
update user secret
|
||||||
|
|
||||||
|
server.logout(username)
|
||||||
|
user logout
|
||||||
|
|
||||||
|
server.ip(username)
|
||||||
|
return ip when connection establish, or nil
|
||||||
|
|
||||||
|
server.start(conf)
|
||||||
|
start server
|
||||||
|
|
||||||
|
Supported skynet command:
|
||||||
|
kick username (may used by loginserver)
|
||||||
|
login username secret (used by loginserver)
|
||||||
|
logout username (used by agent)
|
||||||
|
|
||||||
|
Config for server.start:
|
||||||
|
conf.expired_number : the number of the response message cached after sending out (default is 128)
|
||||||
|
conf.login_handler(uid, secret) -> subid : the function when a new user login, alloc a subid for it. (may call by login server)
|
||||||
|
conf.logout_handler(uid, subid) : the functon when a user logout. (may call by agent)
|
||||||
|
conf.kick_handler(uid, subid) : the functon when a user logout. (may call by login server)
|
||||||
|
conf.request_handler(username, session, msg, sz) : the function when recv a new request.
|
||||||
|
conf.register_handler(servername) : call when gate open
|
||||||
|
conf.disconnect_handler(username) : call when a connection disconnect (afk)
|
||||||
|
]]
|
||||||
|
|
||||||
|
local server = {}
|
||||||
|
|
||||||
|
skynet.register_protocol {
|
||||||
|
name = "client",
|
||||||
|
id = skynet.PTYPE_CLIENT,
|
||||||
|
}
|
||||||
|
|
||||||
|
local user_online = {}
|
||||||
|
local handshake = {}
|
||||||
|
local connection = {}
|
||||||
|
|
||||||
|
function server.userid(username)
|
||||||
|
-- base64(uid)@base64(server)#base64(subid)
|
||||||
|
local uid, servername, subid = username:match "([^@]*)@([^#]*)#(.*)"
|
||||||
|
return b64decode(uid), b64decode(subid), b64decode(servername)
|
||||||
|
end
|
||||||
|
|
||||||
|
function server.username(uid, subid, servername)
|
||||||
|
return string.format("%s@%s#%s", b64encode(uid), b64encode(servername), b64encode(tostring(subid)))
|
||||||
|
end
|
||||||
|
|
||||||
|
function server.logout(username)
|
||||||
|
local u = user_online[username]
|
||||||
|
user_online[username] = nil
|
||||||
|
if u.fd then
|
||||||
|
gateserver.closeclient(u.fd)
|
||||||
|
connection[u.fd] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function server.login(username, secret)
|
||||||
|
assert(user_online[username] == nil)
|
||||||
|
user_online[username] = {
|
||||||
|
secret = secret,
|
||||||
|
version = 0,
|
||||||
|
index = 0,
|
||||||
|
username = username,
|
||||||
|
response = {}, -- response cache
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function server.ip(username)
|
||||||
|
local u = user_online[username]
|
||||||
|
if u and u.fd then
|
||||||
|
return u.ip
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function server.start(conf)
|
||||||
|
local expired_number = conf.expired_number or 128
|
||||||
|
|
||||||
|
local handler = {}
|
||||||
|
|
||||||
|
local CMD = {
|
||||||
|
login = assert(conf.login_handler),
|
||||||
|
logout = assert(conf.logout_handler),
|
||||||
|
kick = assert(conf.kick_handler),
|
||||||
|
}
|
||||||
|
|
||||||
|
function handler.command(cmd, source, ...)
|
||||||
|
local f = assert(CMD[cmd])
|
||||||
|
return f(...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function handler.open(source, gateconf)
|
||||||
|
local servername = assert(gateconf.servername)
|
||||||
|
return conf.register_handler(servername)
|
||||||
|
end
|
||||||
|
|
||||||
|
function handler.connect(fd, addr)
|
||||||
|
handshake[fd] = addr
|
||||||
|
gateserver.openclient(fd)
|
||||||
|
end
|
||||||
|
|
||||||
|
function handler.disconnect(fd)
|
||||||
|
handshake[fd] = nil
|
||||||
|
local c = connection[fd]
|
||||||
|
if c then
|
||||||
|
c.fd = nil
|
||||||
|
connection[fd] = nil
|
||||||
|
if conf.disconnect_handler then
|
||||||
|
conf.disconnect_handler(c.username)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
handler.error = handler.disconnect
|
||||||
|
|
||||||
|
-- atomic , no yield
|
||||||
|
local function do_auth(fd, message, addr)
|
||||||
|
local username, index, hmac = string.match(message, "([^:]*):([^:]*):([^:]*)")
|
||||||
|
local u = user_online[username]
|
||||||
|
if u == nil then
|
||||||
|
return "404 User Not Found"
|
||||||
|
end
|
||||||
|
local idx = assert(tonumber(index))
|
||||||
|
hmac = b64decode(hmac)
|
||||||
|
|
||||||
|
if idx <= u.version then
|
||||||
|
return "403 Index Expired"
|
||||||
|
end
|
||||||
|
|
||||||
|
local text = string.format("%s:%s", username, index)
|
||||||
|
local v = crypt.hmac64(crypt.hashkey(text), u.secret)
|
||||||
|
if v ~= hmac then
|
||||||
|
return "401 Unauthorized"
|
||||||
|
end
|
||||||
|
|
||||||
|
u.version = idx
|
||||||
|
u.fd = fd
|
||||||
|
u.ip = addr
|
||||||
|
connection[fd] = u
|
||||||
|
end
|
||||||
|
|
||||||
|
local function auth(fd, addr, msg, sz)
|
||||||
|
local message = netpack.tostring(msg, sz)
|
||||||
|
local ok, result = pcall(do_auth, fd, message, addr)
|
||||||
|
if not ok then
|
||||||
|
skynet.error(result)
|
||||||
|
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
|
||||||
|
end
|
||||||
|
|
||||||
|
local request_handler = assert(conf.request_handler)
|
||||||
|
|
||||||
|
-- u.response is a struct { message, version, index }
|
||||||
|
local function retire_response(u)
|
||||||
|
if u.index >= expired_number * 2 then
|
||||||
|
local max = 0
|
||||||
|
local response = u.response
|
||||||
|
for k,p in pairs(response) do
|
||||||
|
if p[3] < expired_number then
|
||||||
|
response[k] = nil
|
||||||
|
else
|
||||||
|
p[3] = p[3] - expired_number
|
||||||
|
if p[3] > max then
|
||||||
|
max = p[3]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
u.index = max + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function do_request(fd, msg, sz)
|
||||||
|
local u = assert(connection[fd], "invalid fd")
|
||||||
|
local msg_sz = sz - 4
|
||||||
|
local session = netpack.tostring(msg, sz, msg_sz)
|
||||||
|
local p = u.response[session]
|
||||||
|
if p then
|
||||||
|
-- session can be reuse in the same connection
|
||||||
|
if p[2] == u.version then
|
||||||
|
u.response[session] = nil
|
||||||
|
p = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if p == nil then
|
||||||
|
local ok, result = pcall(conf.request_handler, u.username, msg, msg_sz)
|
||||||
|
-- NOTICE: YIELD here, socket may close.
|
||||||
|
if not ok then
|
||||||
|
skynet.error(result)
|
||||||
|
result = "\0" .. session
|
||||||
|
else
|
||||||
|
result = result .. '\1' .. session
|
||||||
|
end
|
||||||
|
|
||||||
|
p = { netpack.pack_string(result), u.version, u.index }
|
||||||
|
if u.response[session] then
|
||||||
|
skynet.error(string.format("Conflict session %s", crypt.hexencode(session)))
|
||||||
|
end
|
||||||
|
u.response[session] = p
|
||||||
|
else
|
||||||
|
netpack.tostring(msg, sz) -- request before, so free msg
|
||||||
|
-- resend response, and update index p[3].
|
||||||
|
p[3] = u.index
|
||||||
|
end
|
||||||
|
u.index = u.index + 1
|
||||||
|
-- check connect again
|
||||||
|
if connection[fd] then
|
||||||
|
socketdriver.send(fd, p[1])
|
||||||
|
end
|
||||||
|
retire_response(u)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function request(fd, msg, sz)
|
||||||
|
local ok, err = pcall(do_request, fd, msg, sz)
|
||||||
|
-- not atomic, may yield
|
||||||
|
if not ok then
|
||||||
|
skynet.error(string.format("Invalid package %s : %s", err, netpack.tostring(msg, sz)))
|
||||||
|
if connection[fd] then
|
||||||
|
gateserver.closeclient(fd)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function handler.message(fd, msg, sz)
|
||||||
|
local addr = handshake[fd]
|
||||||
|
if addr then
|
||||||
|
auth(fd,addr,msg,sz)
|
||||||
|
handshake[fd] = nil
|
||||||
|
else
|
||||||
|
request(fd, msg, sz)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return gateserver.start(handler)
|
||||||
|
end
|
||||||
|
|
||||||
|
return server
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
local skynet = require "skynet"
|
local skynet = require "skynet"
|
||||||
local gateserver = require "gamefw.gateserver"
|
local gateserver = require "snax.gateserver"
|
||||||
local netpack = require "netpack"
|
local netpack = require "netpack"
|
||||||
|
|
||||||
local watchdog
|
local watchdog
|
||||||
@@ -53,7 +53,7 @@ local function close_fd(fd)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function handler.close(fd)
|
function handler.disconnect(fd)
|
||||||
close_fd(fd)
|
close_fd(fd)
|
||||||
skynet.send(watchdog, "lua", "socket", "close", fd)
|
skynet.send(watchdog, "lua", "socket", "close", fd)
|
||||||
end
|
end
|
||||||
@@ -89,15 +89,6 @@ function CMD.accept(source, fd)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function CMD.kick(source, fd)
|
function CMD.kick(source, fd)
|
||||||
local c
|
|
||||||
if fd then
|
|
||||||
c = connection[fd]
|
|
||||||
else
|
|
||||||
c = forwarding[source]
|
|
||||||
end
|
|
||||||
|
|
||||||
assert(c)
|
|
||||||
|
|
||||||
gateserver.closeclient(fd)
|
gateserver.closeclient(fd)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user