Websocket的技术背景
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文档
.
优势
在需要消息推送、连接保持、交互效率等要求下, 两种协议的转变将会带来交互方式的不同.
首先, Websocket
协议使用头部压缩技术将头部压缩成2-10字节大小并且包含数据载荷长度, 这显著减少了网络交互的开销并且确保信息数据完整性.
如果假设在一个稳定(可能)的网络环境下将尽可能的减少连接建立开销、身份验证等带来的网络开销, 同时还能拥有比 HTTP
协议更方便的数据包解析方式.
其次, 由于基于 Websocket
的协议的在 请求->回应
上是双向的, 所以不会出现多个请求的阻塞连接的情况. 这也极大程度上减少了正常请求延迟的问题.
最后, Websocket
还能给予开发者更多的连接管控能力: 连接超时、心跳判断等. 在合理的连接管理规划下, 这可提供使用者更优质的开发方案.
API
cf框架中的 httpd
库内置了 Websocket
路由, 提供了上述 Websocket
连接管理能力.
Websocket
路由需要开发者提供一个 lua 版的 class
对象来抽象路由处理的过程, 这样的抽象能简化代码编写难度.
lua class
class
意译为'类'. 是对'对象'的一种抽象描述, 多用于各种面相对象编程语言中. lua没有原生的 class
类型, 但是提供了基本构建的元方法.
cf为了方便描述内置对象与内置库封装, 使用lua table的相关元方法建立了最基本的class模型. 几乎大部分内置库都依赖cf的class库.
同时为了简化 class
的学习成本, 去除了class原本拥有的'多重继承'概念. 将其仅作为 类
定义, 用于完成从 class
-> object
的初始化工作.
更多关于 class
的详情, 请参考Wiki中关于 class
库的 文档
.
Websocket 相关的API
现在我们开始学习 Websocket
与之相关的API
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
更多API
更多关于 Websocket
的API请参考Wiki的(文档)[ https://github.com/CandyMi/co...
].
开始实践
建立路由
首先! 让我们在 script
目录下新建2个文件: main.lua
与 ws.lua
, 然后分别填入下列内容:
-- app/script/ws.lua local class = require "class" local ws = class("websocket") function ws:ctor(opt) self.ws = opt.ws self.send_masked = false self.max_payload_len = 65535 end function ws:on_open() end function ws:on_message(data, typ) end function ws:on_error(error) end function ws:on_close(data) end return ws
-- main.lua local httpd = require "httpd" local app = httpd:new("httpd") app:ws('/ws', require "ws") app:listen("", 8080) app:run()
我们使用 httpd
库启动了一个Web Server, 同时将 ws.lua
内的 class
对象注册为 Websocket
处理对象.
同时, 我们在 Websocket:ctor
方法内部, 为Websocket路由的连接初始化了一些连接信息. 以上为最精简的Websocket路由处理.
开始编写一个简单的Demo
首先, 我们在 ws:on_open
方法内部添加一段定时器代码, 这个定时器用于在连接建立完成之后持续向开发者推送递增消息.
function ws:on_open() local cf = require "cf" local count = 1 self.timer = cf.at(3, function(...) self.ws:send(tostring(count)) count = count + 1 end) print(self.ws, "客户端连接成功.") end
然后, 我们为 ws:on_close
方法添加一段定时器销毁代码用于防止内存泄露.
function ws:on_close(data) if self.timer then self.timer:stop() self.timer = nil end print(self.ws, "客户端关闭了连接.") end
最后, 为每次客户端发送过来的消息执行一次echo回应.
function ws:on_message(data, type) self.ws:send(data, type) print(self.ws, "接受到客户端发送的消息.", data) end
运行 cfadmin
,
让我们使用chrome浏览器点击 这里
, 使用提取码 cgwr
下载 Websocket
客户端插件并且安装.
然后打开刚刚下载的websocket client插件并在其 Websocket Address
处输入我们的连接地址进行连接并且查看服务端的推送消息.
ws1.png
开发者可以在运行 cfadmin
的终端查看连接建立的消息打印.
[candy@MacBookPro:~/Documents/core_framework] $ ./cfadmin [2019/06/18 21:48:36] [INFO] httpd正在监听: 0.0.0.0:8080 [2019/06/18 21:48:36] [INFO] httpd正在运行Web Server服务... [2019/06/18 21:48:39] - ::1 - ::1 - /ws - GET - 101 - req_time: 0.000080/Sec websocket-server: 0x7f9495e01200 客户端连接成功. websocket-server: 0x7f9495e01200 接受到客户端发送的消息. hello world websocket-server: 0x7f9495e01200 客户端关闭了连接.
完整的代码
-- main.lua local httpd = require "httpd" local app = httpd:new("httpd") app:ws('/ws', require "ws") app:listen("", 8080) app:run()
-- app/script/ws.lua local class = require "class" local ws = class("websocket") function ws:ctor(opt) self.ws = opt.ws self.send_masked = false self.max_payload_len = 65535 end function ws:on_open() local cf = require "cf" local count = 1 self.timer = cf.at(3, function(...) self.ws:send(tostring(count)) count = count + 1 end) print(self.ws, "客户端连接成功.") end function ws:on_message(data, type) self.ws:send(data, type) print(self.ws, "接受到客户端发送的消息.", data) end function ws:on_error(error) end function ws:on_close(data) if self.timer then self.timer:stop() self.timer = nil end print(self.ws, "客户端关闭了连接.") end return ws
继续学习
下一章我们将学习cf框架内置的异步库
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Lua Web快速开发指南(5) - 利用template库构建httpd模板引擎
- Lua Web快速开发指南(10) - 利用MQ实现异步任务、订阅/发布、消息队列
- linux 内核开发指南 - 2 开发流程
- 面向Java开发人员的Flex开发指南
- NGINX 开发指南
- 网站图标开发指南
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
阿里巴巴正传:我们与马云的“一步之遥”
方兴东、刘伟 / 江苏凤凰文艺出版社 / 2015-1 / 45.00
十几年来,方兴东与马云每年一次,老友聚首,开怀畅谈,阿里上市前,作者再次与马云深度对话,阿里上市前的布局,深入探讨了一系列人们关心的话题。 本书忠实记录了阿里壮大、马云封圣的历史。作者通过细致梳理和盘点,对阿里巴巴的15年成长史进行了忠实回顾。从海博翻译社到淘宝网,从淘宝商城到天猫,从支付宝到阿里云计算,从拉来软银的第一笔投资到纽交所上市,作者对其中涉及到的人物、细节都有生动展现;对于马云、......一起来看看 《阿里巴巴正传:我们与马云的“一步之遥”》 这本书的介绍吧!
UNIX 时间戳转换
UNIX 时间戳转换
HSV CMYK 转换工具
HSV CMYK互换工具