msggate example

This commit is contained in:
Cloud Wu
2014-07-13 19:36:44 +08:00
parent e1674f04c3
commit 0cdd71c0c2
8 changed files with 448 additions and 25 deletions

View File

@@ -1,5 +1,18 @@
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 cjson = require "cjson"
local crypt = require "crypt"
@@ -43,6 +56,7 @@ local hmac = crypt.hmac64(challenge, secret)
socket.writeline(fd, crypt.base64encode(hmac))
local token = {
server = "sample",
user = "hello",
pass = "password",
}
@@ -53,6 +67,49 @@ socket.writeline(fd, crypt.base64encode(etoken))
print(readline())
socket.close(fd)
----- connect to game server
local input = {}
local fd = assert(socket.connect("127.0.0.1", 8888))
local function readpackage()
local line = table.remove(input, 1)
if line then
return line
end
while true do
local status
status, last = socket.recv(fd, last, input)
if status == nil then
error "Server closed"
end
if not status then
socket.usleep(100)
else
local line = table.remove(input, 1)
if line then
return line
end
end
end
end
local index = 0
local request = 0
local handshake = string.format("%s@%s:%d:%d", crypt.base64encode(token.user), 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())

View File

@@ -1,6 +1,7 @@
local login = require "loginserver"
local json = require "cjson"
local crypt = require "crypt"
local skynet = require "skynet"
local server = {
host = "127.0.0.1",
@@ -8,19 +9,40 @@ local server = {
name = "login_master",
}
local server_list = {}
local user_online = {}
function server.auth_handler(token)
token = json.decode(token)
assert(token.user)
assert(token.pass == "password")
return "sample", token.user
return token.server, token.user
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
local gameserver = server_list[u.server]
skynet.call(gameserver, "lua", "kick", server, uid)
end
local gameserver = assert(server_list[server])
skynet.call(gameserver, "lua", "login", server, uid, secret)
end
function server.logout_handler(server, uid)
local CMD = {}
function CMD.register_gate(source, name)
server_list[name] = source
end
function CMD.logout(source, uid, server)
print(string.format("%s@%s is logout", uid, server))
end
function server.command_handler(command, source, ...)
local f = assert(CMD[command])
return f(source, ...)
end
login(server)

View File

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

View File

@@ -0,0 +1,27 @@
local skynet = require "skynet"
skynet.register_protocol {
name = "client",
id = skynet.PTYPE_CLIENT,
unpack = skynet.tostring,
}
local gate
local CMD = {}
function CMD.init(source , uid, server)
gate = source
skynet.error(string.format("%s is coming", uid))
end
skynet.start(function()
skynet.dispatch("lua", function(_, source, command, ...)
local f = assert(CMD[command])
skynet.ret(skynet.pack(f(source, ...)))
end)
skynet.dispatch("client", function(_,_, msg)
skynet.ret(msg)
end)
end)

187
examples/login/msggate.lua Normal file
View File

@@ -0,0 +1,187 @@
local skynet = require "skynet"
local gateserver = require "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(base64(uid)@base64(server):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)
end
local CMD = {}
function CMD.login(source, server, uid, secret)
local username = crypt.base64encode(uid) .. '@' .. crypt.base64encode(server)
users[username] = {
server = server,
uid = uid,
secret = secret,
index = 0,
request = 0,
reserved = 0,
response = {},
}
end
function CMD.logout(source)
local c = agent[source]
if c then
skynet.call(login_master, "lua", "logout", c.server, 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)

130
lualib/gateserver.lua Normal file
View File

@@ -0,0 +1,130 @@
local skynet = require "skynet"
local netpack = require "netpack"
local socketdriver = require "socketdriver"
local gateserver = {}
local socket -- listen socket
local queue -- message queue
local maxclient -- max client
local client_number = 0
local CMD = setmetatable({}, { __gc = function() netpack.clear(queue) end })
local nodelay = false
local connection = {}
function gateserver.openclient(fd)
if connection[fd] then
socketdriver.start(fd)
end
end
function gateserver.closeclient(fd)
local c = connection[fd]
if c then
connection[fd] = false
socketdriver.close(fd)
end
end
function gateserver.start(handler)
assert(handler.message)
assert(handler.connect)
function CMD.open( source, conf )
assert(not socket)
local address = conf.address or "0.0.0.0"
local port = assert(conf.port)
maxclient = conf.maxclient or 1024
nodelay = conf.nodelay
socket = socketdriver.listen(address, port)
socketdriver.start(socket)
if handler.open then
handler.open(source, conf)
end
end
function CMD.close()
assert(socket)
socketdriver.close(socket)
socket = nil
end
local MSG = {}
function MSG.data(fd, msg, sz)
if connection[fd] then
handler.message(fd, msg, sz)
end
end
function MSG.more()
for fd, msg, sz in netpack.pop, queue do
if connection[fd] then
handler.message(fd, msg, sz)
end
end
end
function MSG.open(fd, msg)
if client_number >= maxclient then
socketdriver.close(fd)
return
end
if nodelay then
socketdriver.nodelay(fd)
end
connection[fd] = true
client_number = client_number + 1
handler.connect(fd, msg)
end
local function close_fd(fd)
local c = connection[fd]
if c ~= nil then
connection[fd] = nil
client_number = client_number - 1
end
end
function MSG.close(fd)
if handler.close then
handler.close(fd)
end
close_fd(fd)
end
function MSG.error(fd, msg)
if handler.error then
handler.error(fd)
end
close_fd(fd)
end
skynet.register_protocol {
name = "socket",
id = skynet.PTYPE_SOCKET, -- PTYPE_SOCKET = 6
unpack = function ( msg, sz )
return netpack.filter( queue, msg, sz)
end,
dispatch = function (_, _, q, type, ...)
queue = q
if type then
MSG[type](...)
end
end
}
skynet.start(function()
skynet.dispatch("lua", function (_, address, cmd, ...)
local f = CMD[cmd]
if f then
skynet.ret(skynet.pack(f(address, ...)))
else
skynet.ret(skynet.pack(handler.command(cmd, address, ...)))
end
end)
end)
end
return gateserver

View File

@@ -1,7 +1,6 @@
local skynet = require "skynet"
local socket = require "socket"
local crypt = require "crypt"
local datacenter = require "datacenter"
local function launch_slave(auth_handler)
local cmd = {}
@@ -75,32 +74,26 @@ local function launch_master(conf)
local port = assert(tonumber(conf.port))
local slave = {}
local balance = 1
local cmd = {}
function cmd.logout(server, uid)
conf.logout_handler(server, uid)
end
if conf.init then
conf.init()
conf.init = nil
end
skynet.dispatch("lua", function(_,source,command, ...)
if command == "register_slave" then
table.insert(slave, source)
skynet.ret(skynet.pack(nil))
else
skynet.ret(skynet.pack(conf.command_handler(command, source, ...)))
end
end)
for i=1,instance do
slave[i] = skynet.newservice(SERVICE_NAME)
skynet.newservice(SERVICE_NAME)
end
skynet.dispatch("lua", function(_,_,command, ...)
local f = assert(cmd[command])
skynet.ret(skynet.pack(f(...)))
end)
local id = socket.listen(host, port)
skynet.error(string.format("login server listen at : %s %d", host, port))
socket.start(id , function(fd, addr)
local s = slave[balance]
balance = balance + 1
if balance > instance then
if balance > #slave then
balance = 1
end
local ok, err = pcall(accept, conf, s, fd, addr)
@@ -112,10 +105,11 @@ local function launch_master(conf)
end
local function login (conf)
local name = conf.name or "login_master"
local name = "." .. (conf.name or "login")
skynet.start(function()
local loginmaster = datacenter.get(name)
local loginmaster = skynet.localname(name)
if loginmaster then
skynet.call(loginmaster, "lua", "register_slave")
local auth_handler = assert(conf.auth_handler)
launch_master = nil
conf = nil
@@ -124,8 +118,8 @@ local function login (conf)
launch_slave = nil
conf.auth_handler = nil
assert(conf.login_handler)
assert(conf.logout_handler)
datacenter.set("login_master", skynet.self())
assert(conf.command_handler)
skynet.register(name)
launch_master(conf)
end
end)

View File

@@ -15,7 +15,6 @@ local handler = {}
function handler.open(source, conf)
watchdog = conf.watchdog or source
maxclient = conf.maxclient or 1024
end
function handler.message(fd, msg, sz)