Skynet入门范例之sproto

examples同一级目录新建目录myexample,该目录下面新建以下几个文件:

config,内容:

 

root = "./"
luaservice = root.."service/?.lua;"..root.."myexample/?.lua"
lualoader = root .. "lualib/loader.lua"
lua_path = root.."lualib/?.lua;"..root.."lualib/?/init.lua;"..root.."myexample/?.lua"
lua_cpath = root .. "luaclib/?.so"
snax = root.."myexample/?.lua;"..root.."test/?.lua"

-- preload = "./examples/preload.lua"    -- run preload.lua before every lua service run
thread = 8
logger = nil
logpath = "."
harbor = 1
address = "127.0.0.1:2526"
master = "127.0.0.1:2013"
start = "main"    -- main script
bootstrap = "snlua bootstrap"    -- The service for bootstrap
standalone = "0.0.0.0:2013"
-- snax_interface_g = "snax_g"
cpath = root.."cservice/?.so"
-- daemon = "./skynet.pid"


proto.lua
,定义通信协议,代码:

local sprotoparser = require "sprotoparser"

local proto = {}

proto.c2s = sprotoparser.parse [[
.package {
    type 0 : integer
    session 1 : integer
}

handshake 1 {
    response {
        msg 0 : string
    }
}

say 2 {
    request {
        name 0 : string
        msg 1 : string
    }
}

quit 3 {}

]]

proto.s2c = sprotoparser.parse [[
.package {
    type 0 : integer
    session 1 : integer
}

heartbeat 1 {}
]]

return proto

main.lua,启动服务的主程序,内容如下:

local skynet = require "skynet"

--
启动服务
(启动函数)
skynet.start(function()
--
启动函数里调用Skynet API开发各种服务
print("======Server start=======")
-- skynet.newservice(name, ...)
启动一个新的 Lua 服务(服务脚本文件名)
--[[ skynet.newservice("service1")
    local svr2 = skynet.newservice("service2");
    skynet.call(svr2, "lua", "set", "key1", "val1111111");
    
    local kv = skynet.call(svr2, "lua", "get", "key1");
    print(kv);
--]]
    skynet.newservice("socket1");

--
退出当前的服务
-- skynet.exit
之后的代码都不会被运行。而且,当前服务被阻塞住的 coroutine 也会立刻中断退出。
skynet.exit()
end)

socket1.lua,接收socket消息的lua服务,内容如下:

local skynet = require "skynet"
require "skynet.manager"    -- import skynet.register
local socket = require "skynet.socket"

local proto = require "proto"
local sproto = require "sproto"

local host

local REQUEST = {}

function REQUEST:say()
    print("say", self.name, self.msg)
end

function REQUEST:handshake()
    print("handshake")
end

function REQUEST:quit()
    print("quit")
end

local function request(name, args, response)
    local f = assert(REQUEST[name])
    local r = f(args)
    if response then
        --
生成回应包
(response是一个用于生成回应包的函数。)
        --
处理session对应问题
        -- return response(r)
    end
end

local function send_package(fd,pack)
    --
协议与客户端对应(两字节长度包头+内容)
    local package = string.pack(">s2", pack)
    socket.write(fd, package)
end

local function accept(id)
    --
每当 accept 函数获得一个新的 socket id 后,并不会立即收到这个 socket 上的数据。这是因为,我们有时会希望把这个 socket 的操作权转让给别的服务去处理。
    --
任何一个服务只有在调用 socket.start(id) 之后,才可以收到这个 socket 上的数据。

    socket.start(id)

    host = sproto.new(proto.c2s):host "package"
    -- request = host:attach(sproto.new(proto.c2s))

    while true do
        local str = socket.read(id)
        if str then
            local type,str2,str3,str4 = host:dispatch(str)

            if type=="REQUEST" then
                -- REQUEST :
第一个返回值为 "REQUEST" 时,表示这是一个远程请求。如果请求包中没有 session 字段,表示该请求不需要回应。这时,第 2 和第 3 个返回值分别为消息类型名(即在 sproto 定义中提到的某个以 . 开头的类型名),以及消息内容(通常是一个 table );如果请求包中有 session 字段,那么还会有第 4 个返回值:一个用于生成回应包的函数。
                local ok, result = pcall(request, str2,str3,str4)

                if ok then
                    if result then
                        socket.write(id, "
收到了
")
                        --
暂时不使用回应包回应

                        -- print("response:"..result)
                        -- send_package(id,result)
                    end
                else
                    skynet.error(result)
                end
            end

            if type=="RESPONSE" then
                -- RESPONSE
:第一个返回值为 "RESPONSE" 时,第 2 3 个返回值分别为 session 和消息内容。消息内容通常是一个 table ,但也可能不存在内容(仅仅是一个回应确认)。
                --
暂时不处理客户端的回应
                print("client response")
            end        

        else
            socket.close(id)
            return
        end
    end
end

skynet.start(function()
    print("==========Socket Start=========")
    local id = socket.listen("127.0.0.1", 8888)
    print("Listen socket :", "127.0.0.1", 8888)

    socket.start(id , function(id, addr)
            --
接收到客户端连接或发送消息()
            print("connect from " .. addr .. " " .. id)

            --
处理接收到的消息
            accept(id)
        end)
    --
可以为自己注册一个别名。(别名必须在 32 个字符以内)
    skynet.register "SOCKET1"
end)

client1.lua,客户端测试类,内容如下:

package.cpath = "luaclib/?.so"
package.path = "lualib/?.lua;myexample/?.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_request(name, args)
    session = session + 1
    local str = request(name, args, session)

    --
解包测试
    -- local host2 = sproto.new(proto.c2s):host "package"
    -- local type,str2 = host2:dispatch(str)
    -- print(type)
    -- print(str2)

    socket.send(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

 

posted @ 2017-10-19 20:13  Mr.R123  阅读(417)  评论(0编辑  收藏  举报