持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第22天,点击查看活动详情
一、前言
websocket是前端比较常用的一种通讯方式,既能解决数据的实时性,又能解决轮询带来的对服务器的性能损耗。
但是可能会遇到这种情况:
我们先假设两个变量,网页tom,服务器jerry,tom向b发起了ws连接请求,jerry答应了,从此tom和jerry直接建立了ws通讯通道。
起初b一直向tom隔三差五得就推送几条消息,tom也都接收到了,tom和jerry保持着良好的关系。
中间可能吵过几次架(指tom或者jerry网络波动),断开了连接,但是tom会马上醒悟过来(指触发重连机制),马上重新向b发起ws连接请求,jerry原谅了tom,tom和jerry又一次建立的新的ws通讯通道。
后面jerry没什么话可说了(指长时间没有消息下发给tom),但tom不为所动,甚至一句消息都不来问下jerry,于是乎jerry生气了,拉黑了tom(指服务器触发了某种机制,将ws服务关闭了),被拉黑后的tom才反应过来,连忙向jerry发过去消息(又一次触发重连机制),结果发现连接不上,tom连不上就一次一次重连(此时会反复触发重连机制),但最终还是没能连上jerry。
如果说,在最后的阶段,tom能是不是给jerry发点消息(指发送心跳包),证明自己还是想和jerry保持联系的,那jerry就不会拉黑tom了。
这就是为什么要有心跳机制的原因。
一本正经的乱扯了个故事,但是大概就是这样子的意思,看个乐呵就行。
下面来看看整体实现的思路吧
二、思路
整体实现思路还是比较简单的,下面用vue2的版本给大家讲一下,其他语言可按下面思路去实现。
1、初始化websocket
我们先创建一个init函数,首先需要判断网页tom有没有资格向服务器jerry发起ws请求,如果有,我们就实例化我们的socket变量,采用WebSocket,将ws地址传入进去就行。
同时我们的网页tom学聪明了,买了四个监听器,分别是
onopen,监听是否连接上了jerry
onerror,监听是否和jerry的联系发生了错误
onclose,监听自己是否关闭了socket
onmessage,监听jerry是否发消息过来了
// init初始化函数
init: function() {
if (typeof WebSocket === 'undefined') {
alert('您的浏览器不支持socket')
} else {
// 实例化socket
this.socket = new WebSocket(
'ws:122.123.1.233:9090/api/ws' // 此处为websocket地址
)
// 监听socket连接
this.socket.onopen = this.open
// 监听socket错误信息
this.socket.onerror = this.error
// 监听socket关闭
this.socket.onclose = this.close
// 监听socket消息
this.socket.onmessage = this.getMessage
}
},
2、tom思考一下四个监听器的用法
(1)open函数
tom建立连接成功后,tom就开启了心跳,定时发消息给jerry
open: function() {
this.longstart() // 成功建立连接后,创建心跳检测
},
(2)error函数
出现错误了,是不是tom我连不上jerry了,这必须得启动重连机制,触发重连函数reconnect
error: function() {
this.reconnect()
},
(3)close函数
这怎么可能,tom怎么会主动断开和jerry的联系,肯定是网络波动了,得赶紧重连,免得jerry担心。
close: function() {
this.reconnect()
},
(4)getMessage函数
jerry发来的消息欸,得解密一下,然后想想是要裱起来自己欣赏还是炫耀一番。 同时要重置心跳。
getMessage: function(msg) {
this.longstart()
const data = JSON.parse(msg.data)
// 转义后,这里接后续逻辑即可
}
3、 tom思考要怎么给jerry重新发起连接请求
(1)重连机制reconnect
首先我们定义四个变量
socketStaus: false, // 断线重新标志位,false可发重连请求,true不可
socketTime: null, // 重连机制定时器
timeoutObj: null, // 心跳机制状态1
serverTimeoutObj: null, // 心跳机制状态2
然后开始写我们的reconnect函数,核心功能其实就是重写一遍连接和挂载操作。
但我们tom不想和jerry建立多条通道(有时上条通道还没正常关闭,又重新连接了,导致挂载了很多通道,一旦有消息下发能直接卡崩网页),tom觉得维持一条通道就够了,所以我们添加了socket.readyState !== 1作为判断,只有在连接未成功的状态下,才会新建通道。
同时tom觉得,太过频繁发起请求,电话费会太贵(反复报错),所以我们加了个定时器,只有当定时器socketTime不存在了和socketStaus不存在了,才会重发请求。
// ws断线重连
reconnect() {
if (!this.socketStaus && !this.socketTime) {
this.socketStaus = true
this.socketTime = setTimeout(() => {
this.socketStaus = false
this.socketTime = null
// 没连接上会一直重连,设置延迟避免请求过多
if (this.socket && this.socket.readyState !== 1) {
this.socket = new WebSocket(
'ws:122.123.1.233:9090/api/ws' // 此处为websocket地址
)
// 监听socket连接
this.socket.onopen = this.open
// 监听socket错误信息
this.socket.onerror = this.error
// 监听socket关闭
this.socket.onclose = this.close
// 监听socket消息
this.socket.onmessage = this.getMessage
}
// }
}, 5 * 1000)
}
},
(2)心跳机制longstart
tom为了提醒自己定时给jerry发消息,就创建了循环定时器,提醒自己每隔30s就给jerry发消息,消息内容是跟jerry约定好的,来证明自己还活着,jerry收到消息后,会马上回复,若超过2s,tom会以为断开了,就自动关闭,触发重连机制。
同时为了避免自己重复设置了定时器,每次调用前,都要清空一下之前的定时器。
// 心跳机制
longstart() {
// 1、通过关闭定时器和倒计时进行重置心跳
clearInterval(this.timeoutObj)
clearTimeout(this.serverTimeoutObj)
// 2、每隔30s向后端发送一条商议好的数据
this.timeoutObj = setInterval(() => {
console.log('重置监测心跳')
const data = { // 与后端商量好心跳要传递的值
packageType: 0
}
if (this.socket && this.socket.readyState === 1) {
this.websocketsend(JSON.stringify(data))
// 3、发送数据 2s后没有接收到返回的数据进行关闭websocket重连
this.serverTimeoutObj = setTimeout(() => {
if (this.socket && this.socket.readyState === 1) {
console.log('没有心跳了....')
this.socket.close()
}
}, 2000)
}
}, 30 * 1000)
},
// 发送数据方法
websocketsend: function(Data) { // 数据发送,给后端心跳包 2s
if (this.socket && this.socket.readyState === 1) {
this.socket.send(Data)
}
},
三、小结
今天给大家伙复盘了下webscoket的实现逻辑,整体思路结构大家伙应该也都清楚了,希望对大家伙有所帮助哈。
ps: 我是地霊殿__三無。