WebSocket

 WebSocket简介

 WebSocket广泛应用于需要实时双向通信的场景,如聊天室、实时通知系统等。在这些场景中,服务器需要主动向客户端推送数据,而客户端也需要及时向服务器发送数据。传统的HTTP协议无法实现这种实时双向通信,而WebSocket协议则能够很好地满足这些需求。

具体实现

在一个Vue3项目中,封装了一个WebSocket工具模块。此工具能够在WebSocket发生错误或意外中断的情况下,自动触发重连逻辑。这一重连过程会持续进行,直至成功恢复与服务器的连接为止,从而确保实时通信的连续性和稳定性。

//设置
interface SocketOptions {
  //心跳间隔
  heartbeatInterval?: number;
  //超时重传
  reconnectInterval?: number;
}

class Socket {
  //路径
  url: string;
  ws: WebSocket | null = null;
  opts: SocketOptions;
  listeners: { [key: string]: Function[] } = {};
  //心跳间隔
  heartbeatInterval: number | null = null;
  // 重连间隔
  reconnectInterval: number | null = null;
  isCloseSoket: Boolean = false; // 是否是手动关闭webSoket
  //构造函数
  constructor(url: string, opts: SocketOptions = {}) {
    this.url = url;
    this.opts = {
      //心跳间隔
      heartbeatInterval: 30000,
      //超时重传
      reconnectInterval: 5000,
      ...opts
    };

    this.init();
  }

  //初始化
  init() {
    this.ws = new WebSocket(this.url);
    this.ws.onopen = this.onOpen.bind(this);
    this.ws.onmessage = this.onMessage.bind(this);
    this.ws.onerror = this.onError.bind(this);
    this.ws.onclose = this.onClose.bind(this);
  }

  //打开
  onOpen(event: Event) {
    console.log('WebSocket opened:', event);
    this.reconnectInterval && clearInterval(this.reconnectInterval);
    this.isCloseSoket = false;
    this.startHeartbeat();
    this.emit('open', event);
  }

  //收到的WebSocket消息
  onMessage(event: MessageEvent) {
    // console.log('WebSocket message received:', event.data);
    this.emit('message', event.data);
  }

  //错误
  onError(event: Event) {
    console.error('WebSocket error:', event);
    this.emit('error', event);
  }

  //重连逻辑中,在连接失败后自动重新连接
  onClose(event: CloseEvent) {
    console.log('WebSocket closed:', event);
    this.stopHeartbeat();
    this.emit('close', event);
    this.reconnectInterval && clearInterval(this.reconnectInterval);
    this.reconnectInterval = null;
    // 手动关闭不触发重连
    if (this.isCloseSoket) {
      return;
    }
    //重连逻辑中,只要没连上就一直重连
    this.reconnectInterval = setInterval(() => {
      console.log('WebSocket正在重连');
      this.init();
    }, this.opts.reconnectInterval);
  }

  //开始心跳检测
  startHeartbeat() {
    if (!this.opts.heartbeatInterval) return;

    this.heartbeatInterval = window.setInterval(() => {
      if (this.ws?.readyState === WebSocket.OPEN) {
        this.ws.send('heartbeat');
      }
    }, this.opts.heartbeatInterval);
  }

  //停止心跳检测
  stopHeartbeat() {
    if (this.heartbeatInterval) {
      clearInterval(this.heartbeatInterval);
      this.heartbeatInterval = null;
    }
  }

  //发送消息
  send(data: string) {
    console.log('send', data);
    if (this.ws?.readyState === WebSocket.OPEN) {
      this.ws.send(data);
    } else {
      console.error('WebSocket is not open. Cannot send:', data);
    }
  }

  //事件监听器注册功能的实现
  on(event: string, callback: Function) {
    if (!this.listeners[event]) {
      this.listeners[event] = [];
    }
    this.listeners[event].push(callback);
  }

  //从事件监听器中移除
  off(event: string) {
    if (this.listeners[event]) {
      delete this.listeners[event];
    }
  }

  //在事件监听器中触发一个指定的事件
  emit(event: string, data: any) {
    this.listeners[event]?.forEach(callback => callback(data));
  }
}

export function useSocket(url: string, opts?: SocketOptions) {
  const socket = new Socket(url, opts);

  // 清除所有监听并断开连接
  function clearSocket() {
    socket.off('open');
    socket.off('message');
    socket.off('error');
    socket.off('close');
    socket.stopHeartbeat();
    socket.isCloseSoket = true;
    socket.ws?.close(); // 关闭WebSocket连接
    console.log('clearSocket-断开连接');
  }

  return {
    socket,
    send: socket.send.bind(socket),
    on: socket.on.bind(socket),
    off: socket.off.bind(socket),
    clearSocket
  };
}

export function getSocketUrl(url: string) {
  // 获取webSocket基地址
  let host = location.host;
  let env = import.meta.env.VITE_APP_ENV;
  let devSocketBaseUrl = 'ws:// XXXXXXXX'; // 开发环境soket基地址
  let socketBaseUrl = env == 'dev' ? devSocketBaseUrl : 'ws://' + host;

  let socketUrl = socketBaseUrl + url;
  return socketUrl;
}

在VUE3中的应用

<script setup lang="ts">
import { useSocket, getSocketUrl } from '@/utils/webSocket';

let webSocketRef: any = ref<WebSocket | null>(null);


// 初始化webSocket
function initWebSocket() {
  clearWebSocket();
  let socketUrl = getSocketUrl(`/XXX/XXX`);
  webSocketRef.value = useSocket(socketUrl);

  let { socket, send } = webSocketRef.value;

  socket.on('message', (msg: any) => {
    console.log('msg:', msg)
  });

  // 发送数据
  send('发送内容')

}

<script/> 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值