平台开发——skynet——⑤网关服务gate

skynet外界通讯简介:

 
有两种
①是游戏客互端使用 TCP 连接接入 skynet 节点。如果你用 skynet 实现一个 web 服务器的话,游戏客户端就可以等价于一个浏览器请求。

称为gate 服务
特征:监听一个 TCP 端口,接受连入的 TCP 连接,并把连接上获得的数据转发到 skynet 内部。
介绍:Gate 可以用来消除外部数据包和 skynet 内部消息包的不一致性。外部 TCP 流的分包问题,是 Gate实现上的约定。Gate 会接受外部连接,并把连接相关信息转发给另一个服务去处理。它自己不做数据处理是因为我们需要保持 gate实现的简洁高效。
外部信息
①连接本身的接入和断开消息(控制信息包),
②连接上的数据包
gate三种工作模式
watchdog 模式 ,由gate 加上包头,同时处理控制信息和数据信息的所有数据;
agent 模式 ,让每个 agent 处理独立连接;
broker模式 ,由一个 broker 服务处理不同连接上的所有数据包。
①一开始,Gate无条件转发这两类消息到同一个处理服务。但对于连接数据包,添加一个包头无疑有性能上的开销。
②所以 Gate 还接收另一种工作模式:把每个不同连接上的数据包转发给不同的独立服务上。每个独立服务处理单一连接上的数据包。
③或者,我们也可以选择把不同连接上的数据包从控制信息包(建立/断开连接)中分离开,但不区分不同连接而转发给同一数据处理服务(对数据来源不敏感,只对数据内容敏感的场合)。
 
无论是哪种模式,控制信息都是交给 watchdog 去处理的,而数据包如果不发给 watchdog 而是发送给 agent 或 broker 的话,则不会有额外的数据头(也减少了数据拷贝)。识别这些包是从外部发送进来的方法是检查消息包的类型是否为 PTYPE_CLIENT。当然,你也可以自己定制消息类型让 gate 通知你。
注意:Gate只负责读取外部数据,但不负责回写。也就是说,向这些连接发送数据不是它的职责范畴。

②是第三方的服务,比如数据库服务,它接受一个或多个 TCP 连接。你需要从 skynet 内部建立一个 TCP 连接出去使用。
 
 

skynet网关服务简介

       网关服务 (GateSever) 是游戏的接入层, 基本功能是管理客户端的连接, 分割完整的数据包, 转发给逻辑处理服务.
       skynet 提供了一个通用模板 lualib/snax/gateserver.lua. 同时基于 gateserver.lua, 实现了一个网关服务 gate.lua.
       TCP 是面向字节流的协议,我们需要把字节流流切割成数据包, 具体的方式见分包.

skynet网络服务器一般是用 gate / watchdog / agent 三剑客
为什么有了socket还要用这个?这一点还没完全搞明白,留作以后解决。

其中 watchdog.lua在文件夹 skynet/examples 下 gate.lua 在 skynet/service 文件夹下,gateserver.lua 在 skynet/lualib/snax 文件夹下。

我们写网关服务的时候,gate直接用example里面的,因为它已经被标准化了。
watchdog也直接用,不用修改。
只有agent服务需要自己写。

gate、watchdog、agent协作方式

       首先一个连接进来,先到gate,gate会给watchdog发一个请求。watchdog就会启动一个agent。agent启动以后会给gate发个请求forward,gate就会给连接加上agent属性。当这个连接再有数据进来的时候,还是经过gate,但是gate检查到这个连接已经有agent属性以后,数据就直接发给agent了,不会再发给watchdog。

平台开发——skynet——⑤网关服务gate_第1张图片
简化图
平台开发——skynet——⑤网关服务gate_第2张图片

从gate基础说起

        skynet 提供了一个通用模板 lualib/snax/gateserver.lua 来启动一个网关服务器,通过 TCP 连接和客户端交换数据。 ​
        TCP 基于数据流,但一般我们需要以带长度信息的数据包的结构来做数据交换。gateserver做的就是这个工作,把数据流切割成包的形式转发到可以处理它的地址。

gate服务基础

local gateserver = require "snax.gateserver"   
local handler = {}        --必须提供一张表,表里面定义connect、message等相关回调函数
​
-- 注册各种handler函数接口,比如:handler.connect、handler.disconnect、handler.message...
​
gateserver.start(handler)  --网关服务的入口函数

gate服务实例

local skynet = require "skynet"
local gateserver = require "snax.gateserver"
​
local handler = {}--当一个客户端链接进来,gateserver自动处理链接,并且调用该函数,必须要有
function handler.connect(fd, ipaddr)   
    skynet.error("ipaddr:",ipaddr,"fd:",fd,"connect")
    gateserver.openclient(fd) --链接成功不代表马上可以读到数据,需要打开这个套接字,允许fd接收数据
end
​
--当一个客户端断开链接后调用该函数,必须要有
function handler.disconnect(fd)   
    skynet.error("fd:", fd, "disconnect")
end
​
--当fd有数据到达了,会调用这个函数,前提是fd需要调用gateserver.openclient打开
function handler.message(fd, msg, sz)
    skynet.error("recv message from fd:", fd)
​
end
​
gateserver.start(handler)

启动gate服务 并 需要告诉它监听端口

local gateserver = skynet.newservice("mygateserver")

skynet.call(gateserver, "lua", "open", {   --需要给网关服务发送open消息,来启动监听
        port = 8002,            --监听的端口
        maxclient = 64,         --客户端最大连接数
        nodelay = true,         --是否延迟TCP
})

关闭gate服务

local skynet = require "skynet"
​
local gateserver = ...
skynet.start(function()
    skynet.call(gateserver, "lua",  "close")
    skynet.exit()
end)

客户端tcp连接gate监听端口
注意两点:
①snax.gateserver 基于TCP协议包装了一个两字节数据长度协议

前两个字节表示数据包的长度len(不计算这两个表示长度的字节),高字节在前低字节在后(大端序),后面紧跟len字节数的数据。

也就说,你给gate服务发消息,消息格式必须符合该协议,不然不会识别该消息!
消息打包方法:

local netpack = require "skynet.netpack" --使用netpack

--(发送端)打包数据str,返回一个C指针msg,sz,申请内存
netpack.pack(str)--(接收端)解包数据,返回一个lua的字符串,会释放内存
netpack.tostring(msg, sz)

客户端实例

local skynet = require "skynet"
local socket = require "skynet.socket"
local netpack = require "skynet.netpack"

skynet.start(function()
            local addr = "127.0.0.1:8002"
            skynet.error("connect ".. addr)
            local id  = socket.open(addr)
            assert(id)
            socket.write(id, netpack.pack("Hello world"))
end)

 

介绍gete服务的那些handle们

详细代码参考

--当一个新客户端被accept后,connect 方法会被回调。 fd 是socket句柄 (不是系统fd). ipaddr是客户端地址, 例如 "127.0.0.1:8000".
function handler.connect(fd, ipaddr)   

--当一个客户端断开链接后调用该函数,必须要有
function handler.disconnect(fd)   

--当fd有数据到达了,会调用这个函数,前提是fd需要调用gateserver.openclient打开
function handler.message(fd, msg, sz)

--如果你希望在监听端口打开的时候,做一些初始化操作,可以提供 open 这个方法。
--source 是请求来源地址,conf 是开启 gate 服务的参数表(端口,连接数,是否延迟)。
function handler.open(source, conf)

--当一个连接异常(通常意味着断开),error 被调用,除了 fd ,还会拿到错误信息 msg(通常用于 log 输出)。
function handler.error(fd, msg)

--当 fd 上待发送的数据累积超过 1M 字节后,将回调这个方法。你也可以忽略这个消息。
function handler.warning(fd, size)

--gateserver除了能接收socket消息以为,当然也是可以接受skynet的lua消息,并且gateserver还对lua消息注册函数进行了封装,只需提供handler.command回调函数就能处理lua消息,不需要我们自己调用skynet.dispatch来注册。

function CMD.kick(source, fd)

function handler.command(cmd, source, ...)
    local f = assert(CMD[cmd])
    return f(source, ...)
end

具体的实现参考

其中 watchdog.lua 在文件夹 skynet/examples 下 gate.lua 在
skynet/service文件夹下,gateserver.lua 在 skynet/lualib/snax 文件夹下。

gate服务里面启动一个agent

gate服务写法

--注册client消息专门用来将接收到的网络数据转发给agent,不需要解包,也不需要打包
skynet.register_protocol {   
    name = "client",
    id = skynet.PTYPE_CLIENT,
}

function handler.connect(fd, ipaddr)   
    gateserver.openclient(fd)
    local agent = skynet.newservice("myagent", fd) --连接成功就启动一个agent来代理
    agents[fd] = agent
end
​
function handler.disconnect(fd) --断开连接后,agent服务退出
    local agent = agents[fd]
    if(agent) then
        --通过发送消息的方式来退出不要使用skynet.kill(agent)
        skynet.send(agent, "lua", "quit")
        agents[fd] = nil
    end
end
​
function handler.message(fd, msg, sz)
    local agent = agents[fd]
    skynet.redirect(agent, 0, "client", 0, msg, sz) --收到消息就转发给agent
end

agent写法

skynet.start(function()
    --注册client消息专门用来接收网络数据
    skynet.dispatch("client", function(_,_, msg) 
        func1(msg)
    end)
​
    skynet.dispatch("lua", function(_,_, cmd) --注册lua消息,来退出服务
        if cmd == "quit" then
            skynet.error(fd,"agent quit")
            skynet.exit()
        end
    end)
end)

小结

个人看法:skynet网关服务这一块主要是为了解决网络通信的问题,处理网络请求,目前了解的还不够深,如果想更好的理解,应该仔细研究
注: 这个模板不可以和 Socket 库一起使用。因为这个模板接管了 socket 类的消息。

watchdog.lua 在文件夹 skynet/examples 下
gate.lua 在 skynet/service 文件夹下
gateserver.lua 在 skynet/lualib/snax 文件夹下

本篇博客参考:
https://blog.csdn.net/hp_cpp/article/details/107364207
https://blog.csdn.net/qq769651718/article/details/79435075
https://github.com/cloudwu/skynet/wiki/GateServer

你可能感兴趣的:(平台开发,skynet)