• WebSocket is already in CLOSING or CLOSED state


    WebSocket is already in CLOSING or CLOSED state. 报错信息的解决方案

    今天遇到这样一些错误,中文的意思是 websocket 已经关闭了
    在这里插入图片描述
    简单查了一些资料分析如下

    为什么会断开

    WebSocket断开的原因有很多,服务端客户端主动断开(服务器宕机服务停止,客户端关闭网页),网络不稳定被动断开(物理层断开,或者网络底层数据错误)

    我们可以将错误日志打印出来

    ws.onclose = function (e) {
      logger.error('websocket 断开');
      logger.info(e.code + ' ' + e.reason + ' ' + e.wasClean);
    }
    
    • 1
    • 2
    • 3
    • 4

    WS 断开时,会触发CloseEvent, CloseEvent会在连接关闭时发送给使用 WS 的客户端. 它在 WS 对象的 onclose 事件监听器中使用。

    CloseEvent有三个字段需要注意, 通过分析这三个字段,一般就可以找到断开原因

    • CloseEvent.code: code是错误码,number
    • CloseEvent.reason: reason是断开原因,string
    • CloseEvent.wasClean: wasClean表示是否正常断开,boolean,异常断开时,该值为false

    解决方案1

    监测到断开后,客户端重新发出连接

    如果还是没有链接成功,界面提示用户,由于网络问题,需要重新打开浏览器,或者请检查网络
    在这里插入图片描述

    例如:这是服务端 server 代码

    const WebSocket = require('ws');
    const ws = new WebSocket.Server({ server });
    
    ws.on('connection', function connection(socket) {
      // 当接受消息,打印日志
      socket.on('message', function incoming(data) {
        console.log('Incoming data ', data);
      });
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这是客户端 client 代码

    // 创建 WS 链接
    const socket = new WebSocket('ws://localhost:8080'); 
    
    // 监听连接事件
    socket.addEventListener('open', function(event) {
      console.log("Connected to server");
    });
    
    // 监听消息事件
    socket.addEventListener('message', function(event) {
      console.log('Message from server ', event);
    });
    
    // 发送数据
    var data = {name: 'admin'};
    socket.send(JSON.stringify(data));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    可以在发送数据前,检查一下是否链接

    function isOpen(ws) {
      return ws.readyState === ws.OPEN
    }
    
    if (isOpen(socket)) {
      var data = {name: 'admin'};
      socket.send(JSON.stringify(data));
    } else {
      logger.info('web socket is closing, reconnecting');
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    解决方案2

    这是参考另一个思路,设置心跳监测连接,断线重连(借鉴 https://www.cnblogs.com/gxp69/p/11736749.html 的思路)

    var lockReconnect = false;  // 避免ws重复连接
    var ws = null;          // 判断当前浏览器是否支持WebSocket
    var wsUrl = socketUrl;
    
    // 连接ws
    createWebSocket(wsUrl);
    
    /**
     * 创建 WS 实例
     * @param {string} url ws的URL
     */
    function createWebSocket(url) {
      try {
        if ('WebSocket' in window) {
          ws = new WebSocket(url);
        }
        initEventHandle();
      } catch(e) {
        reconnect(url);
        console.log(e);
      }
    }
    
    /**
     * 初始化事件处理
     */
    function initEventHandle() {
      ws.onclose = function () {
        reconnect(wsUrl);
        console.log("WS 连接关闭!" + new Date().toLocaleString());
      };
      ws.onerror = function () {
        reconnect(wsUrl);
        console.log("WS 连接错误!");
      };
      ws.onopen = function () {
        //心跳检测重置
        heartCheck.reset().start();
        console.log("WS 连接成功!" + new Date().toLocaleString());
      };
      ws.onmessage = function (event) {
        //如果获取到消息,心跳检测重置
        //拿到任何消息都说明当前连接是正常的
        heartCheck.reset().start();
        console.log("WS 收到消息啦:" +event.data);
        if (event.data != 'pong') {
          let data = JSON.parse(event.data);
        }
      };
    }
    
    // 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload = function() {
      ws.close();
    }
    
    /**
     * 重连
     * @param {string} url ws的URL
     * @returns null
     */
    function reconnect(url) {
      if (lockReconnect) return;
      lockReconnect = true;
      // 没连接上,会一直重连,设置延迟 2s 避免连接频繁
      setTimeout(function () {
        createWebSocket(url);
        lockReconnect = false;
      }, 2000);
    }
    
    // 心跳检测
    var heartCheck = {
      timeout: 1000,
      timeoutObj: null,
      serverTimeoutObj: null,
      reset: function() {
        clearTimeout(this.timeoutObj);
        clearTimeout(this.serverTimeoutObj);
        return this;
      },
      start: function() {
        var self = this;
        this.timeoutObj = setTimeout(function() {
          //这里发送一个心跳,后端收到后,返回一个心跳消息,
          //onmessage拿到返回的心跳就说明连接正常
          ws.send("ping");
          console.log("ping!")
          //如果超过一定时间还没重置,说明后端主动断开了
          self.serverTimeoutObj = setTimeout(function() {
            //如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次
            ws.close();
          }, self.timeout)
        }, this.timeout)
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96

    参考链接

    Stack Overflow

    心跳监测介绍

    CSDN

    CSDN

  • 相关阅读:
    sealos爆火后依然要开发sealer,背后原因有哪些?
    分布式架构演进过程
    excel实战应用案例100讲(十一)-Excel插入图片小技巧
    RocketMQ运维工具
    基于MATLAB的电流、电压互感器特性的仿真分析
    Python 元类详解
    【余贞侠】- c语言程序设计第二、三章课后习题答案
    【Rust日报】2022-07-26 lurk - strace written in rust
    python经典百题之画直线
    leetcode-分发饼干
  • 原文地址:https://blog.csdn.net/weixin_41697143/article/details/126543145