Skynet基礎入門例子詳解(7)

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)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章