GateServer的使用
skynet 提供了一個通用模板 lualib/snax/gateserver.lua 來啓動一個網關服務器,通過 TCP 連接和客戶端交換數據。
TCP 基於數據流,但一般我們需要以帶長度信息的數據包的結構來做數據交換。gateserver 做的就是這個工作,把數據流切割成包的形式轉發到可以處理它的地址。
skynet 提供了一個 lua 庫 netpack ,用來把 tcp 流中的數據解析成 長度 + 內容的包。
gateserver就是使用netpack進行包解析。
在同一個目錄建立7個文件(config,proto.lua,main.lua,mygate.lua,socket1.lua,service1.lua,client1.lua)
本例子的client1使用前兩節的client,代碼稍作修改。
client1.lua代碼:
package.cpath = "luaclib/?.so"
package.path = "lualib/?.lua;myexample/e5/?.lua"
if _VERSION ~= "Lua 5.3" then
error "Use lua 5.3"
end
local socket = require "clientsocket"
-- 通信協議
local proto = require "proto"
local sproto = require "sproto"
local host = sproto.new(proto.s2c):host "package"
local request = host:attach(sproto.new(proto.c2s))
local fd = assert(socket.connect("127.0.0.1", 8888))
local session = 0
-- 封包(長度+內容)
local function send_package(fd, pack)
local package = string.pack(">s2", pack)
socket.send(fd, package)
end
local function send_request(name, args)
session = session + 1
local str = request(name, args, session)
-- socket.send(fd, str)
-- 添加數據長度包頭,gateserver接收自動判斷類型爲data(gateserver使用了netpack進行解析)
-- skynet 提供了一個 lua 庫 netpack ,用來把 tcp 流中的數據解析成 長度 + 內容的包。
send_package(fd,str);
print("Request:", session)
end
send_request("handshake")
send_request("say", { name = "soul", msg = "hello world" })
while true do
-- 接收服務器返回消息
local str = socket.recv(fd)
-- print(str)
if str~=nil and str~="" then
print("server says: "..str)
-- socket.close(fd)
-- break;
end
-- 讀取用戶輸入消息
local readstr = socket.readstdin()
if readstr then
if readstr == "quit" then
send_request("quit")
-- socket.close(fd)
-- break;
else
-- 把用戶輸入消息發送給服務器
send_request("say", { name = "soul", msg = readstr })
end
else
socket.usleep(100)
end
end
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
socket1.lua代碼:
local skynet = require "skynet"
local common = require "common"
local gate
skynet.start(function()
print("==========Socket Start=========")
print("Listen socket :", "127.0.0.1", 8888)
-- Socket服務配置
local conf = {
address = "127.0.0.1",
port = 8888,
maxclient = 1024,
nodelay = true,
}
-- common:dump(conf)
-- 啓動Socket管理網關
gate=skynet.newservice("mygate")
-- 打開監聽端口
skynet.call(gate, "lua", "open" , conf)
end)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
mygate.lua代碼:
local skynet = require "skynet"
local gateserver = require "snax.gateserver"
local netpack = require "netpack"
local common = require "common"
local connection = {} -- fd -> connection : { fd , ip }
local handler = {}
local agentlist = {}
-- 當一個完整的包被切分好後,message 方法被調用。這裏 msg 是一個 C 指針、sz 是一個數字,表示包的長度(C 指針指向的內存塊的長度)。
function handler.message(fd, msg, sz)
print("===========gate handler.message============"..fd)
-- common:dump(connection[fd])
local c = connection[fd]
local agent = agentlist[fd]
if agent then
-- skynet.redirect(agent, c.client, "client", 1, msg, sz)
print("接收到客戶端消息,傳給agent服務處理")
else
print("沒有agent處理該消息")
end
end
function handler.connect(fd, addr)
print("===========gate handler.connect============")
local c = {
fd = fd,
ip = addr,
}
-- common:dump(c)
-- 保存客戶端信息
connection[fd] = c
-- 馬上允許fd 接收消息(由於下面交給service1處理消息,所以可以在service1準備好再調用)
-- 這樣可能導致客戶端發來的消息丟失,因爲service1未準備好的情況下,無法處理消息
gateserver.openclient(fd)
agentlist[fd] = skynet.newservice("service1")
skynet.call(agentlist[fd], "lua", "start", { fd = fd, addr = addr })
end
function handler.disconnect(fd)
print(fd.."-斷開連接")
end
function handler.error(fd, msg)
print("異常錯誤")
end
gateserver.start(handler)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
service1.lua代碼:
local common = require "common"
local skynet = require "skynet"
require "skynet.manager" -- import skynet.register
local CMD = {}
local client_fd
local host
-- TODO
-- skynet.register_protocol
-- 註冊新的消息類別 PTYPE_CLIENT,新的類別必須提供 pack 和 unpack 函數,用於消息的編碼和解碼。
function CMD.start(conf)
print("service1 CMD.start")
-- common:dump(conf)
-- SOCKET處理
end
function CMD.disconnect()
-- todo: do something before exit
skynet.exit()
end
skynet.start(function()
print("==========Service1 Start=========")
skynet.dispatch("lua", function(session, address, cmd, ...)
print("==========Service1 dispatch============"..cmd)
local f = CMD[cmd]
skynet.ret(skynet.pack(f(...)))
end)
end)