WebSocket心跳重连讲解

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WebSocket心跳重连讲解相关的知识,希望对你有一定的参考价值。

参考技术A 最近在开发小程序用到了WebSocket,小程序提供了相应的原生API,与H5的API使用方式上有一些区别,所以流行的H5的一些成熟的类库使用起来有些困难,而原生API又存在一些缺陷,所以就自己实现了一套心跳重连机制。

惯例,先简单介绍一下Websocket。

HTTP 协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信请求只能由客户端发起,服务端对请求做出应答处理。

所以当我们想服务器主动给客户端发送消息,HTTP是做不到的,我们只能使用轮询或者长轮询来实现类似的功能,这样的方式效率低并且浪费资源,为了解决这样的问题,WebSocket诞生了。

把你给懒得!自己官网看去!

在使用原生WebSocket的时候,我们经常会感觉不太稳定,服务端发送的消息有时候客户端接收不到,或者是客户端发送的消息服务端接收不到,虽然WebSocket也提供了onError和onClose的方法,但是经常会有各种未知情况导致断开连接而并不触发Error或Close事件。这样就导致实际连接已经断开了,而客户端和服务端却不知道,还在傻傻的等着消息来。

所以我们要解决的问题就很清晰了:

保证连接状态,连接断开时让客户端与服务端都能知道,进而重连。

页面载入后,我们连接socket先

然后调用initEventHandle来绑定各种各样的事件

这个先放在这我们一会往里填东西

我们设置一个锁和最大的重连次数,避免出现无限重连的情况,为了不给服务器太大的压力我这里设置的是5秒重试一次,最多请求12次。

改造一下initEventHandle这样我们就可以实现一般的触发Error的断线重连。

先撸为敬

心跳对象内timeout为每10秒发一次心跳,timeoutObj、serverTimeoutObj是清除定时器用的对象,reset方法重置定时器,start发送心跳。

继续改造我们的initEventHandle

uniapp即时聊天 websocket封装(建立连接断线重连心跳机制主动关闭)

  1. 使用 SocketTask 的方式去管理 webSocket 链接,每一条链路的生命周期都更加可控
  2. 可实现主动建立连接、心跳防断线机制、断线主动重连、提供主动断开的方法

一、如何使用 (uniapp Vue3)

<template>
  // ...
</template>

<script setup>
  import WS from './websocket'
  import  onLoad, onUnload  from '@dcloudio/uni-app'
  
  // 进入聊天页面初始化
  let ws = null
  onLoad((options) => 
    ws = new WS(
      // 连接websocket所需参数
      data:  userId: options.userId ,
      // 首次连接成功之后,断线重新连接后也会触发(防止断线期间对方发送消息未接收到)
      onConnected: () => 
        // toDo
        // 一般用于请求历史消息列表 getHistoryList()
      ,
      // 监听接收到服务器消息
      onMessage: (data) => 
        // toDo
        // 一般用于将最新的一条消息展示在页面上
      
    )
  )
  
  // 发送消息
  function sendMsg() 
    uni.request(
      url: '后端url',
      data: ...,
      success: () => 
        // 发送成功后,上方onMessage会接收到最新消息
      
    )
  
  
  // 页面销毁,断开websocket
  onUnload(() => 
    // 主动关闭websocket
    ws.close()
  )
</script>

二、websocket类代码

// 心跳间隔、重连websocket间隔,5秒
const interval = 5000
// 重连最大次数
const maxReconnectMaxTime = 5

export default class WS 
  constructor(options) 
    // 配置
    this.options = options
    // WS实例
    this.socketTask = null

    // 连接中
    // 是否是正常关闭
    this.normalClose = false
    // 重新连接次数
    this.reconnectTime = 1
    // 重新连接Timer
    this.reconnectTimer = null
    // 心跳Timer
    this.heartTimer = null

    // 发起连接
    this.initWS()

    // 关闭WS
    this.close = () => 
      this.normalClose = true
      this.socketTask.close()
      clearInterval(this.heartTimer)
    
  

  initWS() 
    // this.options.data 连接websocket所需参数
    const url = 'wss://后端url' + this.options.data.userId
    this.socketTask = uni.connectSocket( url, success()  )
    // 监听WS
    this.watchWS()
  

  watchWS() 
    // 监听 WebSocket 连接打开事件
    this.socketTask.onOpen(() => 
      console('websocket连接成功!')
      // 连接成功
      this.options.onConnected()
      // 重置连接次数
      this.reconnectTime = 1
      // 发送心跳
      this.onHeartBeat()
      // 监听消息
      this.onMessage()
      // 关闭Toast
      uni.hideLoading()
    )

    // 监听websocket 错误
    this.socketTask.onError((res) => 
      this.socketTask.close()
      this.onDisconnected(res)
    )

    // 监听 WebSocket 连接关闭事件
    this.socketTask.onClose((res) => 
      // 非正常关闭
      if (!this.normalClose) 
        this.onDisconnected(res)
      
    )
  

  // 监听消息
  onMessage() 
    // 监听websocket 收到消息
    this.socketTask.onMessage((res) => 
      //收到消息
      if (res.data) 
        this.options.onMessage(JSON.parse(res.data))
       else 
        console('未监听到消息:原因:', JSON.stringify(res))
      
    )
  

  // 断开连接
  onDisconnected(res) 
    console('websocket断开连接,原因:', JSON.stringify(res))
    // 关闭心跳
    clearTimeout(this.heartTimer)
    // 全局Toast提示,防止用户继续发送
    uni.showLoading( title: '消息收取中…' )
    // 尝试重新连接
    this.onReconnect()
  

  // 断线重连
  onReconnect() 
    clearTimeout(this.reconnectTimer)
    if (this.reconnectTime < maxReconnectMaxTime) 
      this.reconnectTimer = setTimeout(() => 
        console.log(`第【$this.reconnectTime】次重新连接中……`)
        this.initWS()
        this.reconnectTime++
      , interval)
     else 
      uni.showModal(
        title: '温馨提示',
        content: '服务器开小差啦~请返回聊天列表重试',
        showCancel: false,
        confirmText: '我知道了',
        success: () => 
          uni.navigateBack()
        
      )
    
  

  /** @心跳 **/
  onHeartBeat() 
    this.heartTimer = setInterval(() => 
      this.socketTask.send(
        data: `heart:$this.options.data.userId`,
        success() 
          console.log('心跳发送成功!')
        
      )
    , interval)
  


以上是关于WebSocket心跳重连讲解的主要内容,如果未能解决你的问题,请参考以下文章

初探和实现WebSocket心跳重连

WebSocket 心跳检测和重连机制

uniapp websocket原生服务(自动重连心跳检测) Ba-Websocket

js websocket 心跳检测,断开重连,超时重连

websocket封装使用心跳检测断线重连

webSocket使用心跳包实现断线重连