websocket

前言

我们先来看一下心跳检测机制?

        心跳检测机制是一种用于检测系统、应用程序或网络连接状态的技术。基本原理是定期发送小型数据包(称为心跳包或心跳消息)以确认远程设备或连接仍然处于活动状态。如果一段时间内没有收到心跳响应,系统就会认为连接已断开或设备不再可用。

心跳机制几个关键概念

  1. 心跳包:数据包 => 通常是一个标识或消息(ping-pong)
  2. 心跳间隔:心跳包发送的时间间隔
  3. 心跳响应:客户端或者服务器接收心跳包时,做出的响应
  4. 维护状态:心跳检测机制通常在服务器端维护状态
  5. 超时处理:心跳检测机制认定为不活跃、超时并做处理(自动重连机制)

为什么需要心跳机制?

  • 检测连接状态
  • 维护连接性
  • 管理在线状态
  • 检测系统、设备健康状况
  • 减少资源浪费

实现思路

  1. 客户端发出建立连接 new WebSocket(socketUrl)
  2. 添加连接状态、消息接收、错误信息、关闭等事件监听
  3. 在socket.onopen 建立连接之后开始定期发送心跳包

提问

为什么需要pong响应机制?

        之所以需要 Pong 响应机制,是因为在 WebSocket 连接客户端和服务端之间发送数据时,底层的网络传输有可能会发生数据包丢失或延迟的情况,这可能导致客户端发送的 Ping 数据包无法成功送达服务端。如果只依靠客户端发送 Ping 数据包来保持 WebSocket 连接的活跃状态,那么不能可靠地判断连接是否正常。

        Pong 响应机制可以通过检查服务端返回的 Pong 数据包,判断服务端是否正常连接。如果客户端在指定时间内未接收到服务端返回的 Pong 数据包,则可以判断连接已断开,从而执行相应的重连操作,从而保证 WebSocket 连接的稳定性和可靠性,并防止因连接长时间闲置而被服务端断开的情况发生。

是否监听ping响应失败可以执行重连?

        ping响应失败无法判断服务端是否断开。有可能是因为网络传输问题(网络差)导致数据包丢失或者延迟情况导致发送失败,所以需要pong响应机制判断服务端的维护状态。

代码1:双向通信

export default class WebsocketClient {
  constructor(url, openCallback, errorCallback, msgCallback) {
    this.url = url;
    this.openCallback = openCallback || function() { };
    this.errorCallback = errorCallback || function() { };
    this.msgCallback = msgCallback || function() { };
    this.socket = null;
    this.connected = false;
    this.pingInterval = 30; // 发送心跳包的时间间隔,单位为秒
    this.pongTimeoutLimit = 3; // 超过 Pong 超时次数的限制值
    this.pongTimeoutCount = 0; // 超过 Pong 超时的次数
    this.pongTimeout = this.pingInterval + 10; // 判断连接是否正常的超时时间,单位为秒,应该比 pingInterval 稍长,以便确保正常连接不被判定为断开状态
    this.lastPingTime = 0; // 上次发送 Ping 数据的时间戳
    this.connect();
  }

  connect() {
    const self = this;
    this.socket = uni.connectSocket({
      url: this.url,
      success() {},
      fail() {
        self.reconnect();
      }
    });
    this.socket.onOpen(() => {
      console.log('WebSocket连接成功!');
      self.connected = true;
      self.openCallback();
      self.startPing();
    });
    this.socket.onMessage((res) => {
      console.log('收到服务器消息:' + res.data);
      let message = {};
      try {
        message = JSON.parse(res.data);
      } catch (e) {
        console.log('消息格式错误!');
      }
      if (message.action === 'pong') {
        console.log('接收到pong数据包,连接正常!');
        this.pongTimeoutCount = 0; // pong 超时次数清零
      } else {
        self.msgCallback(message);
      }
    });
    this.socket.onError(() => {
      console.log('WebSocket连接错误!');
      self.errorCallback();
      self.reconnect();
    });
    this.socket.onClose(() => {
      console.log('WebSocket连接关闭!');
      self.connected = false;
      self.reconnect();
    });
  }

  startPing() {
    const self = this;
    this.pingTimer = setInterval(() => {
      console.log('发送Ping数据包');
      if (self.connected) {
        self.socket.send({data: JSON.stringify({action: 'ping'})});
        self.lastPingTime = new Date().getTime();
        self.pongTimeoutCount++; // 记录pong超时次数
        if (self.pongTimeoutCount >= self.pongTimeoutLimit) {
          console.log('WebSocket连接已超时(' + self.pongTimeout + '秒内未能接收到pong数据包),正在重新连接...');
          self.stop();
          self.reconnect();
        }
      }
    }, self.pingInterval * 1000);
  }

  stop() {
    clearInterval(this.pingTimer);
    this.pongTimeoutCount = 0;
  }

  reconnect() {
    const self = this;
    if (self.connected) {
      return;
    }
    this.stop();
    setTimeout(() => {
      console.log('正在重新连接 WebSocket...');
      self.connect();
    }, 5000);
  }

  close() {
    this.stop();
    uni.closeSocket();
    console.log('WebSocket已关闭!');
  }
}

代码2:服务端推送信息

/**
 * 发起websocket请求函数
 * @param {string} url - websocket请求地址
 * @param {number} timeout  - 心跳间隔时长,默认5000ms
 * @param {function} msgCallback  - 接收到ws数据,对数据进行处理的回调函数
 */
export default class WS {
  constructor(url, msgCallback, timeout = 30) {
    this.url = url;
    this.data = null;
    // 心跳检测
    this.timeout = timeout;
    this.interVal = null;
    this.reconnectTimer = null;
    this.msgCallback =
      msgCallback ||
      function (res) {
        return res;
      };
	this.connectSocketInit();
  }

  connectSocketInit() {
    this.socketTask = uni.connectSocket({
      url: this.url,
      success: (res) => {
        console.log(this.url + "ws连接成功", res);
      }, //请求成功
      fail: (res) => {
        console.log("ws连接失败", res);
      },
    });

    this.socketTask.onOpen(() => {
      console.log("WebSocket连接正常打开中...!");
      this.handleHeart();
    });
    this.socketTask.onMessage((e) => {
      console.log("onMessage---------->", e);
      //接收消息
      try {
        let result = JSON.parse(e.data);
        this.msgCallback(result);
      } catch (err) {
        console.log("err", err);
      }
    });
    this.socketTask.onError((e) => {
      console.log("取号服务WebSocket连接发生错误", e);
      this.reconnect();
    });
    this.socketTask.onClose(() => {
      console.log("关闭websocket");
      this.reconnect();
    });
  }

  //心跳检测
  handleHeart() {
    var that = this;
    this.interVal = setInterval(function () {
      that.send();
    }, this.timeout * 1000);
  }
  //发送消息
  send() {
    var that = this;
    var data = {
      state: 1,
      method: "ping",
    };
    this.socketTask.send({
      data: JSON.stringify(data),
      fail(res) {
        console.log("发送失败,重新连接");
        that.reconnect();
      },
    });
  }
  // 重新连接socket
  reconnect() {
   this.clearTimer()
    this.reconnectTimer = setTimeout(() => {
      this.connectSocketInit();
    }, 3000);
  }
  clearTimer(){
	  this.reconnectTimer && clearTimeout(this.reconnectTimer);
	  this.interVal && clearInterval(this.interVal);
  }
  close() {
	this.clearTimer()
    uni.closeSocket();
    this.socketTask = null;
  }
}

参考:WebSocket聊天室在线状态管理:心跳检测机制(附源码) - 掘金

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值