介绍
WebSocket
是一种在单个TCP连接上进行全双工通信的协议, WebSocket
通信协议于2011年被IETF定为标准RFC 6455
并由RFC7936
补充规范.
WebSocket
使得客户端和服务器之间的数据交换变得更加简单, 使用WebSocket
的API只需要完成一次握手
就直接可以创建持久性的连接并进行双向数据传输.
WebSocket
支持的客户端不仅限于浏览器
(Web应用), 在现今应用市场内的众多App客户端的长连接推送服务都有一大部分是基于WebSocket
协议来实现交互的.
Websocket
由于使用HTTP协议升级而来, 在协议交互初期需要根据正常HTTP协议交互流程. 因此, Websocket也很容易建立在SSL数据加密技术的基础上进行通信.
协议
WebSocket
与HTTP协议实现类似但也略有不同. 前面提到: WebSocket
协议在进行交互之前需要进行握手
, 握手协议
的交互就是利用HTTP协议
升级而来.
众所周知, HTTP协议是一种无状态的协议. 对于这种建立在请求->回应
模式之上的连接, 即使在HTTP/1.1
的规范上实现了Keep-alive
也避免不了这个问题.
所以, Websocket
通过HTTP/1.1
协议的101
状态码进行协议升级协商, 在服务器支持协议升级的条件下将回应升级请求来完成HTTP->TCP
的协议升级
.
原理
客户端将在经过TCP3次握手之后发送一次HTTP升级连接请求, 请求中不仅包含HTTP交互所需要的头部信息, 同时也会包含Websocket
交互所独有的加密信息.
当服务端在接受到客户端的协议升级请求的时候, 各类Web服务实现的实际情况, 对其中的请求版本、加密信息、协议升级详情进行判断. 错误(无效)的信息将会被拒绝.
在两端确认完成交互之后, 双方交互的协议将会从抛弃原有的HTTP协议转而使用Websocket
特有协议交互方式. 协议规范可以参考RFC文档.
cf框架提供了websocket client与server库.
API
websocket server
首先, 创建一个Websocket的class. 如下所示:
- local class = require "class"
- local websocket = class('ws')
WebSocket:ctor(opt)
初始化Websocket对象, Websocket客户端连接建立完成之前被调用.
此方法在on_open方法之前被调用, 一般用于告诉httpd
应该如何怎么进行数据包交互.
- function websocket:ctor (opt)
- self.ws = opt.ws -- websocket对象
- self.send_masked = false -- 掩码(默认为false, 不建议修改或者使用)
- self.max_payload_len = 65535 -- 最大有效载荷长度(默认为65535, 不建议修改或者使用)
- end
WebSocket:on_open()
当有连接初始化完成之后此方法会被调用. 此方法虽然与Websocket:ctor
类似, 但一般在仅用于内部服务初始化的时候使用.
- function websocket:on_open()
- local cf = require "cf"
- self.timer = cf.at(0.01, function ( ... ) -- 启动一个循环定时器
- self.count = self.count + 1
- self.ws:send(tostring(self.count))
- end)
- end
WebSocket:on_message(data, type)
此方法将在用户主动发送text/binary数据的时候被回调.
参数data是一个字符串类型的playload; type是一个boolean类型变量, true为binary类型, 否则为text类型.
- function websocket:on_message(data, typ)
- print('on_message', self.ws, data, typ)
- self.ws:send('welcome')
- -- self.ws:close(data)
- end
WebSocket:on_error(error)
此方法在发生协议错误与未知错误的时候会被回调, 参数error是字符串类型的错误信息.
通常情况下我们不会用到这个方法.
- function websocket:on_error(error)
- print('on_error:', error)
- end
WebSocket:on_close(data)
此方法在连接关闭时回调. data为关闭连接时发送过来到数据, 所以data可能为nil
.
无论什么情况, 在连接被关闭的时候都将会调用此方法, 而此方法通常的作用是清理数据.
- function websocket:on_close(data)
- if self.timer then -- 清理定时器
- print("清理定时器")
- self.timer:stop()
- self.timer = nil
- end
- end
websocket client
创建一个websocket-client对象.
- 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"}
1. w:connect()
此方法用于创建websocket client对象后连接至url指定的server端.
此方法返回为true时连接成功, 否则err为失败原因.
- local ok, err = w:connect()
- if ok then
- return print(err)
- end
- print("连接成功")
2. w:set_timeout(timeout)
此方法用于给client库设置最大recv等待时间. 默认情况下会一直等待.
- w:set_timeout(30)
3. w:ping(data)
此方法用于给server端发送ping.
data为字符串类型payload.
- w:ping('ping')
4. w:pong(data)
此方法用于给server端发送pong.
data为字符串类型payload.
- w:pong('pong')
5. w:recv()
此方法用来获取服务端的发送的数据.
此方法返回data, typ; data为服务端发送的数据, typ为命令类型(text/binary/ping/pong等等).
- while 1 do
- local data, typ = w:recv()
- if not data then
- return w:close()
- end
- print(data, typ)
- end
6. w:send(data, binary)
此方法用于给server端发送数据.
data为字符串类型payload, binary是一个boolean类型变量, true为binary类型, 否则为text类型.
- -- w:send('hello world', true)
- w:send('hello world')
注意: 请不要对w对象进行异步数据发送. 如:
- local cf = require "cf"
- while 1 do
- local data = w:recv()
- if not data then
- return w:close
- end
- cf.fork(function ()
- w:send(data)
- end)
- end
这可能会导致不可预料的错误出现.
7. w:close()
当您不再使用client对象时请用此方法关闭.