websocket心跳机制和整体实现思路

435 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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: 我是地霊殿__三無。