一、websocket是什么
WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被[W3C]定为标准。它算是html5规范中的一个部分,算是一种协议,它借鉴了socket这种思想,为web应用程序客户端和服务端之间(注意是客户端服务端)提供了一种全双工通信机制。同时,它又是一种新的应用层协议,websocket协议是为了提供web应用程序和服务端全双工通信而专门制定的一种应用层协议,通常它表示为:ws://echo.websocket.org/?encoding=text HTTP/1.1,可以看到除了前面的协议名和http不同之外,它的表示地址就是传统的url地址。(ws是不加密的,wss是加密的)
那这种协议是怎么和传统的HTTP1.0或者HTTP/1.1区分开的呢,我从使用过程中截取了一张图片。
可以看到websocket发起的请求,connection是upgrade,还额外多了upgrade的属性,属性值为websocket,而正常情况短连接的请求,connection都是close的,这才符合http协议下,一个request对应一个response的定义。
服务器也能根据这两个字段,区分websocket请求和其他http请求。
connection: upgrade
urgrade: websocket
简而言之,websocket就是一种新的协议,用来解决实时通信的。首次通信过程借用http的握手动作,建立通道,然后用tcp进行数据传输。
二、为什么要用websocket
在没有websocket之前,开发者们是如何解决这个实时通信的场景的呢。
1、ajax轮询
过程:连接->传输数据->关闭连接
开个定时器,每隔几秒就发起一次请求,询问服务端是否有新数据。
这种方法,一是对服务器压力大(连续请求压力能不大吗),必须要有很快的处理速度,快速响应,会让服务器cpu资源经常被占用,二是总归是有时差(存在间隔时间),要缩短间隔时间做到伪实时通信,那又对服务器压力激增。
2、long poll 长轮询
过程:连接->传输数据->关闭连接
原理和第一点的ajax轮询差不多,都是客户端主动向服务器发起请求,区别就是long poll采用的是阻塞模型,顾名思义,就是会一直占线,直到服务器有消息或者触发超时机制,才会返回response给到客户端,周而复始。
这种方法,发起请求的数量比ajax可少得多了,不过就是会一直占线,这要求服务器必须有很高的并发能力,不然可能会出现,其他正常请求服务器处理不过来的情况。
3、长连接
过程:连接->传输数据->保持连接 -> 传输数据-> ………..->一方关闭连接
HTTP1.1默认是长连接,也就是默认Connection的值就是keep-alive,本次请求响应结束后,TCP连接将仍然保持打开状态,所以浏览器可以继续通过相同的连接发送请求,节省了很多TCP连接建立和断开的消耗,还节约了带宽。
这种长连接的方法,存在几个缺点
a、 keep-alive确实可实现长连接,但是本质上这还是从客户端发起-服务端应答,这种一request一response的模式,不过是省略了每次请求的开启和关闭操作。
b、长连接不代表是永久连接,如果超时了,且连接中没有新消息发出,那这个长连接就会断掉,相应的tcp连接也会被干掉。
4、webscoket
过程:连接->传输数据->保持连接 -> 传输数据-> ………..->一方关闭连接
在WebSocket中,只需要服务器和浏览器通过HTTP协议进行一个握手的动作,然后单独建立一条TCP的通信通道进行数据的传送。WebSocket同HTTP一样也是应用层的协议,但是它是一种双向通信协议,是建立在TCP之上的。
WebSocket的流程大概是以下几步:
a、浏览器、服务器建立TCP连接,三次握手。这是通信的基础,传输控制层,若失败后续都不执行。
b、TCP连接成功后,浏览器通过HTTP协议向服务器传送WebSocket支持的版本号等信息。(开始前的HTTP握手)服务器收到客户端的握手请求后,同样采用HTTP协议回馈数据。确保tcp通信通道建立成功。
c、连接成功后,双方通过TCP通道进行数据传输,不需要HTTP协议。 也就是说WebSocket在建立握手时,数据是通过HTTP传输的。但是建立之后,在真正传输时候是不需要HTTP协议的。
相比上面3种,websocket连接成功后,是能双向传输数据的,既可以是服务端主动发起数据推送,也可以是客户端主动向服务端推送心跳包(划重点)。打破了以往web端,只能一问一答,还得是客户端主动发起请求的模式。这对于实时性要求高的场景来说,在项目中接入webscoket,既确保了实时性,对资源要求也不高,无疑是个很好的选择。
websocket目前的缺点就是,低版本浏览器不兼容。(不过现在应该也不会有人用什么ie9或者更低版本吧)
三、怎么在vue项目中使用
要使用webscoket(项目不需要额外引入),那就得对它各种相关状态,api属性都有一定了解。
首先我们先创建一个websocket对象
// 实例化socket,第一个参数必填,第二个是可选的参数,指定子协议用的
this.socket = new WebSocket(
url, [protocol]
)
复制代码
那这个socket对象就有以下属性
调用方法都是this.socket.【属性名】
还具备了相关事件,可供调用
发送数据方法
知道了这些,下面我们就来vue项目中实操一下吧。
# 由于本人仅是前端,本文就不阐述后端websocket的具体实现,仅叙述前端的实现过程
第一步
在data中创建好相关变量
socket: '', // websocket对象
socketStaus: false, // 断线重新标志位,false可发重连请求,true不可
timeoutObj: null, // 心跳机制状态1
serverTimeoutObj: null, // 心跳机制状态2
复制代码
第二步
创建webscoket初始化函数init,在其中进行socket的实例化,和其他相关事件的监听
init: function() {
if (typeof WebSocket === 'undefined') {
alert('您的浏览器不支持socket') // ie9及以下版本不支持,判断一下
} else {
// 实例化socket
this.socket = new WebSocket(
this.config.data[this.GLOBAL_CONST.MONITOR.WS_URL_FOR_WEB],
) // 括号中的值是一个url
// 监听socket连接
this.socket.onopen = this.open // 关联到具体函数
// 监听socket错误信息
this.socket.onerror = this.error // 关联到具体函数
// 监听socket关闭
this.socket.onclose = this.close // 关联到具体函数
// 监听socket消息
this.socket.onmessage = this.getMessage // 关联到具体函数
}
},
复制代码
第三步
创建init中相关调用函数
几个函数如下
open: function() {
this.longstart() // 成功建立连接后,创建心跳检测
},
error: function() {
// console.log('连接错误,开始重新连接')
this.reconnect()
},
close: function() {
console.log('socket已经关闭,开始重新连接')
this.reconnect()
},
// ws断线重连
reconnect() {
if (!this.socketStaus) {
this.socketStaus = true
setTimeout(() => {
console.log('重连 实例化socket 前', this.config)
// 没连接上会一直重连,设置延迟避免请求过多,设了5秒限流
this.socket = new WebSocket(
this.config.data[this.GLOBAL_CONST.MONITOR.WS_URL_FOR_WEB],
)
// 监听socket连接
this.socket.onopen = this.open
// 监听socket错误信息
this.socket.onerror = this.error
// 监听socket关闭
this.socket.onclose = this.close
// 监听socket消息
this.socket.onmessage = this.getMessage
this.socketStaus = false
}, 5 * 1000)
}
},
复制代码
还有最重要的接收数据的函数
// ws获取到数据执行方法
getMessage: function(msg) {
this.longstart() // 拿到数据也要重置心跳机制
try {
const data = JSON.parse(msg.data)
console.log(msg.data, data)
// 这里写具体逻辑
} catch (e) {
console.log('出错了', e)
return
}
},
复制代码
心跳函数
// 心跳机制 建议接入该机制
在使用websocket的过程中,有时候会遇到网络断开的情况,但是在网络断开的时候服务
器端并没有触发onclose的事件。这样可能会发生服务器会继续向客户端发送数据,并且
这些数据会丢失。所以需要一种机制来检测客户端和服务端是否处于正常的链接状态。因
此就有了websocket的心跳,检测链接的状态。
longstart() {
// 1、通过关闭定时器和倒计时进行重置心跳
clearInterval(this.timeoutObj)
clearTimeout(this.serverTimeoutObj)
// 2、每隔30s向后端发送一条商议好的数据
this.timeoutObj = setInterval(() => {
console.log('重置监测心跳')
const data = { // 与后端商量好心跳要传递的值
packageType: 0
}
this.websocketsend(JSON.stringify(data))
// 3、发送数据 2s后没有接收到返回的数据进行关闭websocket重连
this.serverTimeoutObj = setTimeout(() => {
console.log('没有心跳了....')
this.socket.close()
}, 2000)
}, 30 * 1000)
},
复制代码
发送数据的函数
// 发送数据方法
websocketsend(Data) { // 数据发送
this.socket.send(Data)
},
复制代码
第四步
在合适的时机调用init初始化socket对象
比如我项目是一打开就要连接websocket,那我就在挂载阶段调用。
mounted() {
this.init()
}
复制代码
第五步
退出前记得销毁相关socket对象和心跳定时器
beforeDestroy() {
// 1、组件销毁时,关闭与服务器的连接
if (this.socket) {
this.socket.close() // 离开路由之后断开websocket连接
}
// 销毁心跳定时器
if (this.timeoutObj) {
clearInterval(this.timeoutObj)
this.timeoutObj = null
}
if (this.serverTimeoutObj) {
clearTimeout(this.serverTimeoutObj)
this.serverTimeoutObj = null
}
}
复制代码
到此,vue项目已经接入websocket啦,小伙伴们有需要可以试一下。
ps,这里是地霊殿-三無,希望能对你们有所帮助。
作者:地霊殿__三無
链接:https://juejin.cn/post/7124942304690831368
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。