GateServer

cloudwu edited this page Mar 21, 2016 · 9 revisions

skynet 提供了一个通用模板 lualib/snax/gateserver.lua 来启动一个网关服务器,通过 TCP 连接和客户端交换数据。

TCP 基于数据流,但一般我们需要以带长度信息的数据包的结构来做数据交换。gateserver 做的就是这个工作,把数据流切割成包的形式转发到可以处理它的地址。

用法

local gateserver = require "snax.gateserver"

local handler = {}

-- register handlers here

gateserver.start(handler)

这样就可以启动一个网关服务。handler 是一组自定义的消息处理函数,分别有:

function handler.connect(fd, ipaddr)

当一个新连接建立后,connect 方法被调用。传入连接的 socket fd 和新连接的 ip 地址(通常用于 log 输出)。

function handler.disconnect(fd)

当一个连接断开,disconnect 被调用,fd 表示是哪个连接。

function handler.error(fd, msg)

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

function handler.command(cmd, source, ...)

如果你希望让服务处理一些 skynet 内部消息,可以注册 command 方法。收到 lua 协议的 skynet 消息,会调用这个方法。cmd 是消息的第一个值,通常约定为一个字符串,指明是什么指令。source 是消息的来源地址。这个方法的返回值,会通过 skynet.ret/skynet.pack 返回给来源服务。

open 和 close 这两个指令是保留的。它用于 gate 打开监听端口,和关闭监听端口。

function handler.open(source, conf)

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

function handler.message(fd, msg, sz)

当一个完整的包被切分好后,message 方法被调用。这里 msg 是一个 C 指针、sz 是一个数字,表示包的长度(C 指针指向的内存块的长度)。注意:这个 C 指针需要在处理完毕后调用 C 方法 skynet_free 释放。(通常建议直接用封装好的库 netpack.tostring 来做这些底层的数据处理);或是通过 skynet.redirect 转发给别的 skynet 服务处理。

function handler.warning(fd, size)

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

在这些方法中,还可以调用 gateserver 模块的方法如下:

gateserver.openclient(fd)   -- 允许 fd 接收消息

每次收到 handler.connect 后,你都需要调用 openclient 让 fd 上的消息进入。默认状态下, fd 仅仅是连接上你的服务器,但无法发送消息给你。这个步骤需要你显式的调用是因为,或许你需要在新连接建立后,把 fd 的控制权转交给别的服务。那么你可以在一切准备好以后,再放行消息。

gateserver.closeclient(fd) -- 关闭 fd

通常用于主动踢掉一个连接。

wire protocol

这里尽可能的做最简单的约定:

每个包就是 2 个字节 + 数据内容。这两个字节是 Big-Endian 编码的一个数字。数据内容可以是任意字节。

所以,单个数据包最长不能超过 65535 字节。如果业务层需要传输更大的数据块,请在上层业务协议中解决。

netpack api

lualib-src/lua-netpack.c 是处理这类数据包的库。

local netpack = require "netpack"

可以加载这个库。

  • netpack.pack(msg, [sz]) 把一个字符串(或一个 C 指针加一个长度)打包成带 2 字节包头的数据块。这个 api 返回一个lightuserdata 和一个 number 。你可以直接送到 socket.write 发送(socket.write 负责最终释放内存)。
  • netpack.tostring(msg, sz) 把 handler.message 方法收到的 msg,sz 转换成一个 lua string,并释放 msg 占用的 C 内存。

netpack 还有一些内部 api 用于 gate server 的实现。

Gate 服务器

service/gate.lua 是一个实现完整的网关服务器,同时也可以作为 snax.gateserver 的使用范例。examples/watchdog.lua 是一个可以参考的例子,它启动了一个 service/gate.lua 服务,并将处理外部连接的消息转发处理。

gate 服务启动后,并非立刻开始监听。要让 gate 服务器开启监听端口,可以通过 lua 协议向它发送一个 open 指令,附带一个启动参数表,下面是一个示范:

skynet.call(gate, "lua", "open", {
    address = "127.0.0.1", -- 监听地址 127.0.0.1
    port = 8888,    -- 监听端口 8888
    maxclient = 1024,   -- 最多允许 1024 个外部连接同时建立
    nodelay = true,     -- 给外部连接设置  TCP_NODELAY 属性
})

注: 这个模板不可以和 Socket 库一起使用。因为这个模板接管了 socket 类的消息。

其它方案

skynet 并不限制你怎样编写网关,比如你还可以使用这个模块:http://blog.codingnow.com/2016/03/skynet_tcp_package.html