Websocket使用指南
cf提供了websocket client与server库, 具体API使用请参考Websocket API
server handle使用示例
cf的websocket协议层实现将其作为一种升级协议内嵌到httpd库内. 所以, 当您使用httpd库的时候即可很方便的使用websocket.
我们根据cf web的初始化与使用最后的示例, 在script/main.lua
文件内添加一段代码:
app:ws('/ws', require 'ws')
然后我们在script
目录建立一个ws.lua
文件, 在其内部添加如下代码:
local class = require "class"
local cf = require "cf"
local json = require "json"
local websocket = class("websocket")
function websocket:ctor(opt)
self.ws = opt.ws -- websocket对象
self.send_masked = false -- 掩码(默认为false, 不建议修改或者使用)
self.max_payload_len = 65535 -- 最大有效载荷长度(默认为65535, 不建议修改或者使用)
self.count = 0
end
function websocket:on_open()
print('on_open')
self.timer = cf.at(0.01, function ( ... ) -- 定时器
self.count = self.count + 1
self.ws:send(tostring(self.count))
end)
end
function websocket:on_message(data, typ)
print('on_message', self.ws, data)
self.ws:send('welcome')
-- self.ws:close(data)
end
function websocket:on_error(error)
print('on_error', self.ws, error)
end
function websocket:on_close(data)
print('on_close', self.ws, data)
if self.timer then -- 清理定时器
print("清理定时器")
self.timer:stop()
self.timer = nil
end
end
return websocket
上述流程做了些什么?
我们利用app:ws
为httpd对象导入了一个叫websocket的lua class, 这个class定义了websocket handle所需的所有方法.
httpd库实现了websocket需要的: on_open
、on_message
、on_error
、on_close
等方法, 这些方法对应用户定义的websocket class方法.
在websocket ctor方法中, httpd会为开发者传入一个websocket对象. 这个对象目前有2个公有方法可供开发者使用: send方法与close方法.
首先会才连接建立的时候会调用ctor
进行初始化, 这个初始化的可选操作是让用户定义send_masked
、max_payload_len
、timeout
.
其中timeout默认为永不超时(nil), 除非开发者指定超时时间; max_payload_len指定最大数据包长度; send_masked表示是否需要进行掩码处理;
上述三个内置参数都推荐使用默认值或者不指定, 而超时时间一般推荐websocket server开发者主动构建应用层心跳包并根据是否回应来决定是否断开连接.
在初始化完成之后, httpd建立连接完成将会再次调用on_open
方法, 这个方法一般用于用户注册、初始化一些与websocket class本身无关的一些资源(例如MQ).
值得注意的是: 在这个on_open
执行完成之前, httpd库将不会接受任何websocket消息. 在生产环境运行中的业务需要特别注意.
每当httpd server接受到一个客户端发送一个消息过来, 将会调用on_message
方法将数据传入进来. 第一个参数是数据载荷, 第二个参数是数据类型;
用户可以使用ctor
调用时传入到send与close方法来决定是否应该回应消息与关闭消息(ping类型的消息由框架自动回应).
on_error
是传输协议错误或其它原因导致的异常将会将error信息导入进来, 一般正常websocket消息交互与关闭将不会触发此方法.
on_close
一般会在客户端关闭、网络连接中断、服务端主动关闭的时候主动触发, 留给开发者释放资源的机会.
client 使用示例
首先导入wc库, 然后初始化一url后进行连接. url支持ipv6与ipv4或者hostname方式.
示例代码在script/test_wsclient.lua
.
local wc = require "protocol.websocket.client"
-- local w = wc:new {url = "wss://[::1]/ws"}
-- local w = wc:new {url = "wss://[::1]:8080/ws"}
local w = wc:new {url = "ws://localhost:8080/ws"}
local ok, ret = w:connect()
if not ok then
return print(ok, ret)
end
w:ping('ping')
print(w:recv())
while 1 do
local data, typ = w:recv()
print(data, typ)
if not data then
break
end
end
w:close()
websocket client 使用new方法初始化传参, url参数将会被自动解析为有效域名、端口、路径;
websocket client支持ws与wss两种连接方式. 在初始化对象完成之后, 可以尝试调用connect进行数据连接. 当连接成功时返回true; 否则返回nil与出错信息.
websocket client支持ping、pong、send、close等方法用于数据交互. 用户在所有websocket client完成之后需要调用close方法释放连接.
注意事项
上述两个最简单的例子提示了基于cf框架的websocket业务应该如何编写, 但是在正式环境下应该注意一下几点:
cf是事件驱动框架, 高效稳定的websocket server应该有效合理的利用MQ来处理推送业务与消息订阅服务.
cf框架的消息推送是由队列来保证顺序的, 通常情况下这不会有性能问题. 但是过多的消息将会导致队列长度的自动伸缩, 这无疑对稳定性与延迟无益.
cf框架没限制用户注册的定时器数量, 但是经过无数测试代码证明: 海量高精度定时器(<0.01ms)无法保证开发者有效的利用事件驱动模型和更好的利用CPU、内存.
cf的websocket server/client的send都是异步发送! 所以不会保证一定会发送成功, 即是发送成功也不保证对方一定能收到(这需要依赖于系统配置与应用层来保证).