React + ts 中应用 Web Work 中集成 WebSocket

一、Web Work定义

TypeScript 复制代码
 useEffect(() => {
    let webSocketIndex  = -1
    const websocketWorker = new Worker(new URL('./websocketWorker.worker.ts?worker',             import.meta.url),{
        type: 'module' // 必须声明模块类型
    });
    
    //初始化WEBSOCKET(多个服务器选择最快建立连接的一个)
    const urls = (import.meta.env.VITE_WEB_SOCKET).split(',');
    for(const url of urls) {
      this.works.websocketWorker.postMessage({type: "INIT_WEBSOCKET", payload: {url: url}});
    }

    websocketWorker.onmessage = (e) => {
      //接收子线程消息
      //接收连接打开消息
      if(e.data.type === "OPEN") {
        //这里通过变量改变只使用一次opne选择最快反馈的服务器
        if(webSocketIndex === -1) {
          webSocketIndex = e.data.index;
          websocketWorker.postMessage({type: "SELECT_SOCKET", payload:e.data.index});
        }
      }
    };

    return () => {
        if(websocketWorker ) {
            websocketWorker.postMessage({ type: "CLOSE_WEBSOCKET" });
            const str = setTimeout(() => {
                websocketWorker .terminate()
                clearTimeout(str);
            }, 500);
        }
    };
 }, []);

二、子线程中WebSocket 的启动使用

TypeScript 复制代码
let socket: WebSocket | null = null;
let sockets: WebSocket[] = [];
let pingTimer: NodeJS.Timeout | null = null;

self.onmessage = (e) => {
    const message = e.data as MessageData;
    //webSocket 子进程
    switch(message.type) {
       //初始化webSocket
       case "INIT_WEBSOCKET":
        const ws = new WebSocket(message.payload.url);
        sockets.push(ws);
        ws.onopen = () => self.postMessage({ type: 'OPEN', index: sockets.indexOf(ws) });
        return "";
       case "SELECT_SOCKET":
         socket = sockets[message.payload];
         // 发送Ping帧(心跳检测)
         pingTimer = setInterval(() => {
            (socket as WebSocket).send(new Uint8Array([0x89, 0x00])); 
         }, PING_TIME);
         //消息队列先进先出
         const queue = new WebSocketQueue(socket);
         queue.listen((msg) => {
            console.log('SOCKET:', msg.timestamp, msg.data);
         });
         //其他socket连接关闭节省资源
         const otherSocket = sockets.filter((_, index) => index !== message.payload);
         for(const other of otherSocket) {
            other.close();
         }
         return "";   
       case "CLOSE_WEBSOCKET":
         if(socket) (socket as WebSocket).close();
         if(pingTimer) clearInterval(pingTimer);
         return "";     
       default:
        return "";         
    }
 };

三、补充(WebSocket 消息队列实现)

TypeScript 复制代码
/**
 * WebSocket消息队列,先进先出
 */
export class WebSocketQueue {
  private queue: {data: any, timestamp: number}[] = [];
  private messageHandler?: (msg: {data: any, timestamp: number}) => void;

  constructor(socket: WebSocket) {
    socket.onmessage = (ev) => {
      this.queue.push({data: ev.data, timestamp: Date.now()});
      this.messageHandler?.(this.queue.shift()!); // FIFO处理
    };
  }

  // 注册消息处理器
  public listen(handler: (msg: {data: any, timestamp: number}) => void) {
    this.messageHandler = handler;
  }
}