skynet 的 C API 采用异步读写,你可以使用 C 调用,监听一个端口,或发起一个 TCP 连接。但具体的操作结果要等待 skynet 的事件回调。skynet 会把结果以 PTYPE_SOCKET 类型的消息发送给发起请求的服务。(参考skynet_socket.h)
在处理实际业务中,这样的 API 很难使用,所以又提供了一组阻塞模式的 lua API 用于 TCP socket 的读写。它是对 C API 的封装。
所谓阻塞模式,实际上是利用了 lua 的 coroutine 机制。当你调用 socket api 时,服务有可能被挂起(时间片被让给其他业务处理),待结果通过 socket 消息返回,coroutine 将延续执行。
local socket = require "socket"
这样就可以在你的服务中引入这组 api 。
__gc 元方法中关闭连接的话,shutdown 是一个比 close 更好的选择(因为在 gc 过程中无法切换 coroutine)。socket api 中有两个不同的写操作。对应 skynet 为每个 socket 设定的两个写队列。通常我们只需要用:
但同时 skynet 还提供一个低优先级的写操作(如果你不需要这个设计,可以不使用它):
对于服务器,通常我们需要监听一个端口,并转发某个接入连接的处理权。那么可以用如下 API :
每当 accept 函数获得一个新的 socket id 后,并不会立即收到这个 socket 上的数据,也不能向这个 socket 写数据。这是因为,我们有时会希望把这个 socket 的操作权转让给别的服务去处理。
socket 的 id 对于整个 skynet 节点都是公开的。也就是说,你可以把 id 这个数字通过消息发送给其它服务,其他服务也可以去操作它。任何一个服务只有在调用 socket.start(id) 之后,才可以收到这个 socket 上的数据,或者向这个 socket 写数据。注:skynet 框架是根据调用 start 这个 api 的位置来决定把对应 socket 上的数据转发到哪里去的。
skynet 为 udp 协议做了有限的支持。和 tcp 协议不同,udp 协议不需要阻塞读取。这是因为 udp 是不可靠协议,无法预期下一个读到的数据包是什么(协议允许乱序和丢包)。所以 skynet 的 udp 协议封装采用的是 callback 的方式。
你可以用 socket.udp 这个 API 创建一个 udp handle ,并给它绑定一个 callback 函数。当这个 handle 收到 udp 消息时,callback 函数将被触发。
socket.udp(function(data, size, from), address, port) : id
这个函数会返回一个 handle id 。
socket.udp_connect(id, address, port, callback)
你可以给一个 udp handle 设置一个默认的发送目的地址。当你用 socket.udp 创建出一个非监听状态的 handle 时,设置目的地址非常有用。因为你很难有别的方法获得一个有效的供 socket.sendto 使用的地址串。这里 callback 是可选项,通常你应该在 socket.udp 创建出 handle 时就设置好 callback 函数。但有时,handle 并不是当前 service 创建而是由别处创建出来的。这种情况,你可以用 socket.start 重设 handle 的所有权,并用这个函数设置 callback 函数。
设置完默认的目的地址后,之后你就可以用 socket.write 来发送数据包。
注:handle 只能属于一个 service ,当一个 handle 归属一个 service 时,skynet 框架将对应的网络消息转发给它。向一个 handle 发送网络数据包则不需要当前 service 拥有这个 handle 。
socket.sendto(id, from, data, size)
向一个网络地址发送一个数据包。第二个参数 from 即是一个网络地址,这是一个 string ,通常由 callback 函数生成,你无法自己构建一个地址串,但你可以把 callback 函数中得到的地址串保存起来以后使用。
这个字符串可以用 socket.udp_address(from) : address port 转换为可读的 ip 地址和端口,用于记录。
通过阅读实现代码 lualib/socket.lua ,你可以更清楚的了解 socket api 是如何工作的。你也可能会发现一些并未在上面列出的 api 。它们可能在未来的版本中废弃。
如果你需要一个网关帮你接入大量连接并转发它们到不同的地方处理。lualib/gate.lua 可以直接使用,同时也是用于了解 skynet 的 socket 模块如何工作的不错的参考。它还有一个功能近似的,但是全部用 C 编写的版本 service-src/service_gate.c 。
如果仅仅是了解 socket api 的基本用法,以及搭建一个简单的服务器。请参考 test/testsocket.lua ;udp 部分见 test/testudp.lua 。