websocket 基础

341 阅读4分钟

构造函数

使用 WebSocket 构造函数来构造一个 ws 对象。

ws 对象提供了用于创建和管理 WebSocket 连接,以及可以通过该连接发送和接收数据的 API。

常量
① 正在连接:WebSocket.CONNECTING === 0;(WebSocket.prototype.CONNECTING = 0)
② 连接成功:WebSocket.OPEN === 1;(WebSocket.prototype.OPEN = 1)
③ 连接正在关闭:WebSocket.CLOSING === 2;(WebSocket.prototype.CLOSING = 2)
④ 连接已经关闭,或者打开连接失败:WebSocket.CLOSED === 3。(WebSocket.prototype.CLOSED = 3)

属性

bufferedAmount:只读属性,返回已经被 send( ) 方法放入队列中但还没有被发送到网络中的数据的字节数。(一旦队列中的所有数据被发送至网络,则该属性值将被重置为 0。但是,若在发送过程中连接被关闭,则属性值不会重置为 0。如果你不断地调用 send( ),则该属性值会持续增长)。

ws.bufferedAmount === 0; // 发送完毕;
ws.bufferedAmount > 0; // 发送未结束;

readyState: 只读属性,返回 WebSocket 的链接状态。(值为0~3其中之一)。

ws.readyState === WebSocket.CONNECTING; // 0: 正在链接中;
ws.readyState === WebSocket.OPEN; // 1: 已经链接并且可以通讯;
ws.readyState === WebSocket.CLOSING; // 2: 连接正在关闭;
ws.readyState === WebSocket.CLOSED; // 3: 连接已关闭或者没有链接成功;

方法

close( ):关闭 WebSocket 连接或连接尝试(如果有的话)。 如果连接已经关闭,则此方法不执行任何操作。

ws.close(); // 关闭 WebSocket

send( ):将需要通过 WebSocket 链接传输至服务器的数据排入队列,并根据所需要传输的 data bytes 的大小来增加 bufferedAmount 的值 。若数据无法传输(例如数据需要缓存而缓冲区已满)时,套接字会自行关闭。

ws.send('发送数据~'); // 向服务器发送数据;

事件

onopen:定义一个事件处理程序,当 WebSocket 的连接状态 readyState 变为 1 时调用,这意味着当前连接已经准备好发送和接受数据。这个事件处理程序通过事件(建立连接时)触发。

// 写法一:
ws.onopen = (e) => { console.log('websocket连接成功~'); };
// 写法二:
ws.addEventListener('open', (e)=>{ console.log('websocket连接成功~'); });

onmessage:message 事件会在 WebSocket 接收到新消息时被触发。

// 写法一:
ws.onmessage = (e) => { console.log('websocket接收消息~'); };
// 写法二:
ws.addEventListener('message', (e)=>{ console.log('websocket接收消息~'); });

onclose:返回一个事件监听器,这个事件监听器将在 WebSocket 连接的 readyState 变为 CLOSED 时被调用,它接收一个名字为 close 的 CloseEvent 事件。

// 写法一:
ws.onclose = (e) => { console.log('websocket连接关闭~'); };
// 写法二:
ws.addEventListener('close', (e)=>{ console.log('websocket连接关闭~'); });

onerror:当 websocket 的连接由于一些错误事件的发生 (例如无法发送一些数据) 而被关闭时,一个 error 事件将被引发。

// 写法一:
ws.onerror = (e) => { console.log('websocket报错了~'); };
// 写法二:
ws.addEventListener('error', (e)=>{ console.log('websocket报错了~'); });

完整示例(上)

// src/utils/websocketServer.js:
const readyState = {
    CONNECTING: 0, // 正在连接;
    OPEN: 1, // 连接成功;
    CLOSING: 2, // 连接正在关闭;
    CLOSED: 3, // 连接已经关闭,或者打开连接失败;
};

class WebsocketServer {
    ws = {}; // websocket对象;
    hostAddress = null; // 长连接地址;
    callback = null; // 接收websocket消息的回调方法;
    timer = null;
    connectLimitTimes = 3; // 重连限制次数;

    constructor(opts) {
        const { hostAddress, callback } = opts;
        this.hostAddress = hostAddress;
        this.callback = callback;
    }

    /**
    * 初始化websocket;
    */
    initWebsocket() {
        const { hostAddress } = this;
        this.ws = new WebSocket(hostAddress);
        this.open();
        this.close();
        this.message();
        this.error();
        this.timer = setInterval(() => {
            const ready = this.ws.readyState;
            if (ready === readyState.OPEN) {
                if (this.ws.bufferedAmount === 0) {
                    // 发送完毕;
                    this.send('ping ~ 心跳包');
                } else {
                    // 发送未结束;
                }
            }
            if (ready === readyState.CLOSED) {
                if (this.connectLimitTimes > 0) {
                    this.connectLimitTimes--;
                    this.reconnect();
                } else {
                    clearInterval(this.timer);
                }
            }
        }, 1000);
    }
    /**
    * 连接成功后的回调方法;
    */
    open() {
        this.ws.addEventListener('open', (e) => {
            this.connectLimitTimes = 3;
            console.log('websocket连接成功~');
        });
    }
    /**
    * 长连接关闭后的回调方法;
    */
    close() {
        this.ws.addEventListener('close', (e) => {
            console.log('websocket连接关闭~');
        });
    }
    /**
    * 接收服务器数据后的回调方法;
    */
    message() {
        this.ws.addEventListener('message', (e) => {
            this.callback && this.callback(e);
            console.log('websocket接收消息~', e);
        });
    }
    /**
    * 向服务器发送数据;
    */
    send(msg) {
        this.ws.send(msg);
    }
    /**
    * 报错时的回调方法;
    */
    error() {
        this.ws.addEventListener('error', (e) => {
            console.log('websocket报错了~');
        });
    }
    /**
    * 关闭websocket;
    */
    handleClose() {
        const { ws } = this;
        if (ws.readyState === readyState.OPEN) {
            ws.close();
        }
    }
    /**
    * 重连;
    */
    reconnect() {
        if (this.timer) clearInterval(this.timer);
        this.initWebsocket();
    }
}
export default WebsocketServer;

// src/App.vue:
<script>
export default {
    data(){
        return {
            wsServer: null, // websocket服务;
        }
    },
    mounted(){
        this.initWebsocketServer();
    },
    methods: {
        /**
        * 实例化websocket服务;
        */
        initWebsocketServer() {
            const hostAddress = `ws://192.168.58.204:9090/notice/websocket/20220301/22070062`;
            this.wsServer = new WebsocketServer({
                hostAddress,
                callback(e) {
                    console.log('websocket接收的数据:', e);
                },
            });
            this.wsServer.initWebsocket();
        }
    }
}
</script>

完整示例(下)

对上面的示例进行改进

// src/utils/websocketServer.ts:
const readyState = {
  CONNECTING: 0, // 正在连接;
  OPEN: 1, // 连接成功;
  CLOSING: 2, // 连接正在关闭;
  CLOSED: 3, // 连接已经关闭,或者打开连接失败;
};

class WebsocketServer {
  ws: any = {}; // websocket对象;
  hostAddress: any = null; // 长连接地址;
  timer: any = null;
  connectLimitTimes: number = 0; // 重连限制次数;
  constructor(opts: { hostAddress: any }) {
    const { hostAddress } = opts;
    this.hostAddress = hostAddress;
  }

  /**
   * 初始化websocket;
   */
  initWebsocket(connectedCallback: any, receiveMsgCallback: any, connectFailedCallback: any, connectLimitTimes: number) {
    const { hostAddress } = this;
    this.connectLimitTimes = connectLimitTimes;
    this.ws = new WebSocket(hostAddress);
    this.open(connectedCallback);
    this.close();
    this.message(receiveMsgCallback);
    this.error();
    clearInterval(this.timer);
    this.timer = setInterval(() => {
      const ready = this.ws.readyState;
      if (ready === readyState.OPEN) {
        if (this.ws.bufferedAmount === 0) {
          // 发送完毕;
          // this.send('ping ~ 心跳包');
        } else {
          // 发送未结束;
        }
      }
      if (ready === readyState.CLOSED) {
        if (this.connectLimitTimes > 0) {
          this.connectLimitTimes--;
          this.reconnect(connectedCallback, receiveMsgCallback, connectFailedCallback, this.connectLimitTimes);
        } else {
          connectFailedCallback && connectFailedCallback();
          clearInterval(this.timer);
        }
      }
    }, 100);
  }
  /**
   * 连接成功后的回调方法;
   */
  open(connectedCallback: any) {
    this.ws.addEventListener('open', (e: any) => {
      connectedCallback && connectedCallback(e);
      console.log('websocket连接成功~');
    });
  }
  /**
   * 长连接关闭后的回调方法;
   */
  close() {
    this.ws.addEventListener('close', (e: any) => {
      console.log('websocket连接关闭~');
    });
  }
  /**
   * 接收服务器数据后的回调方法;
   */
  message(receiveMsgCallback: any) {
    this.ws.addEventListener('message', (e: any) => {
      receiveMsgCallback && receiveMsgCallback(e);
      console.log('websocket接收消息~', e);
    });
  }
  /**
   * 向服务器发送数据;
   */
  send(msg: any) {
    this.ws.send(msg);
  }
  /**
   * 报错时的回调方法;
   */
  error() {
    this.ws.addEventListener('error', (e: any) => {
      console.log('websocket报错了~');
    });
  }
  /**
   * 关闭websocket;
   */
  handleClose() {
    const { ws } = this;
    if (ws.readyState === readyState.OPEN) {
      ws.close();
    }
  }
  /**
   * 重连;
   */
  reconnect(connectedCallback: any, receiveMsgCallback: any, connectFailedCallback: any, connectLimitTimes: any) {
    if (this.timer) clearInterval(this.timer);
    this.initWebsocket(connectedCallback, receiveMsgCallback, connectFailedCallback, connectLimitTimes);
  }
}
export default WebsocketServer;

//src/App.vue:
import WebsocketServer from './utils/websocketServer';
import { onMounted, ref } from 'vue';
import bus from '@/utils/bus';

let wsServer = ref();

onMounted(() => {
  localStorage.setItem('ws_host', '127.0.0.1');
  initWebsocketServer();
});

/**
 * 实例化websocket服务;
 */
const initWebsocketServer = () => {
  const hostAddress = `ws://${localStorage.getItem('ws_host')}:21001/printList`;
  wsServer.value = new WebsocketServer({
    hostAddress,
  });
  // @ts-ignore
  window.scmWs = wsServer.value;
};

//src/utils/common.ts:
/**
 * 获取ws状态;
 */
const getWsStatus = () => {
  return window.scmWs.ws.readyState;
};

//src/views/material/materialInfor/hooks/usePurchasConfig.ts:
import { getWsStatus, getPrinterList, isJsonString } from '@/utils/common';

const wsStatus = getWsStatus();
if (wsStatus === 1) {
  getPrinterList();
  getSourceCodeInfo(row);
} else {
  initWs(
    () => {
      getPrinterList();
      getSourceCodeInfo(row);
    },
    (e: any) => {
      if (e.data === 'success') {
      } else if (isJsonString(e.data)) {
        bus.emit('printList', JSON.parse(e.data));
      } else {
        msgError({ msg: e.data });
      }
    },
    () => {
      sourceCodeBtnLoading.value = false;
      msgError({ msg: '请下载并安装打印组件,如果已安装请先打开再打印!' });
    },
    0,
  );
}

拓展

WebSocket
零距离接触 websocket
WebSocket其实没那么难
你不知道的 WebSocket
WebSocket:5分钟从入门到精通
WebSocket探秘
websocket 断线重连