skynet提供了一个通用模板lualib/snax/gateserver.lua来启动一个网关服务器,通过TCP连接和客户端交换数据.
TCP 基于数据流,但一般我们需要以带长度信息的数据包的结构来做数据交换,gateserver做的就是这个工作,把数据流切割成包的心事转发到可以处理它的地址
用法
ocal 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.oack返回给来源服务.
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用于gateserver的实现.
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