WebSocket 知识点

websocket机制

Web Socket 是通过一个长时连接实现与服务器全双工、双向的通信。

一、定义

websocket 使用了自定义协议,不能使用http://或https://,而要使用ws://和wss://。不带s的是不安全的连接,带s的是安全连接。

为什么要使用自定义协议而非HTTP协议,客户端与服务器之间可以发送非常少的数据,不会对HTTP造成任何负担。使用更小的数据包让WS非常适合带宽和延迟问题比较捉鸡的移动应用。WS得到了所有主流浏览器的支持。

创建新的WS,必须实例化ws对象并传入提供连接的URLlet ws = new WebSocket("ws://www.example/websocket"),传入的连接必须是一个绝对的URL。WS不受同源策略影响,因此可以打开到任何站点。如果需要与特定源页面通信,则完全取决于服务器(在握手阶段就可以确定请求来自哪里)。



二、API

与XHR相似,WS也有一个readyState属性表示当前状态。

  • WebSocket.OPENING(0): 连接正在简历
  • WebSocket.OPEN(1): 连接正在建立
  • WebSocket.CLOSEING(2): 连接正在关闭
  • WebSocket.CLOSE(3): 连接已经关闭

任何时候都可以使用ws.close()关闭WS连接,调用后,WS的readyState立刻变为2,关闭后变为3.



三、发送和接受数据

  1. 要向服务器发送数据,使用send()并传入一个字符串、ArrayBuffer或Bolb

    ws.send('123)

  2. 服务器向客户端发送消息时,WS对象上触发一个message事件,可以通过event.data属性访问到有效载荷,与send发送的数据类似,event.data返回的数据可能也是ArrayBuffer或Bolb。这是由WS对象的binaryType属性决定的,该属性可能是'bolb'或'arraybuffer'

ws.onmessage = function(event){
    let data = event.data;
    ...
}


四、生命周期

  • open:在连接成功建立时触发
  • error:在发生错误时触发。连接无法存续
  • close:在连接关闭时触发

但是WebSocket 不支持DOM Level2 事件监听器,即不能使用addEventListener

let ws = new WebSocket("ws://www.example/websocket")
ws.onopen = function(){
    ...
}
ws.onerror = function(){
    ...
}
// 这三个方法只有onclose方法的event对象有额外信息
// 这个对象上有三个额外属性,wasClean、code、reason
ws.onclose = function(event){
    ...
}

这三个事件只有onclose方法的event对象有额外信息,可以将这些信息显示给用户或记录到日志。

这个对象上有三个额外属性,wasClean、code、reason

wasClean是个布尔值,代表连接是不干净的关闭

code是一个来自服务器的数值状态码

reason是一个字符串,包含服务器来的信息。



五、心跳

心跳是为了保证ws通讯不间断的方法简单的说就是医生拿听筒听你的心跳证明你还活着。但是触发方式分为客户端触发和服务端触发。

  1. 客户端触发:如果是前端发送心跳,后端需要返回心跳,也就是ping pong的过程会有两次数据传递。
  2. 服务端触发:后端来发送心跳的话,就只需要发送ping,前端不需要回应。

封装socket.js

class Socket {
    /**
     * @description: 初始化实例属性,保存参数
     * 
     */
    constructor(options) {
        this.url = options.url;
        this.callback = options.received;
        this.name = options.name || 'default';
        this.ws = null;
        this.status = null;
        this.pingInterval = null;
        // 心跳检测频率
        this._timeout = 3000;
        this.isHeart = options.isHeart;
        this.isReconnection = options.isReconnection;
    }
    connect(data) {
        this.ws = new WebSocket(this.url);
        // 建立连接
        this.ws.onopen = (e) => {
            this.status = 'open';
            console.log("连接成功", e)
            if(this.isHeart) {
                // 心跳
                this._heartCheck()
            }
            // 给后台发送数据
            if(data !== undefined) {
                return this.ws.send(JSON.stringify({type: 'init'}))
            }
        }
        // 接受服务器返回的信息
        this.ws.onmessage = (e) => {
            if(typeof this.callback === 'function'){
                return this.callback(e.data)
            }else{
                console.log('参数的类型必须为函数')
            }
        }
        // 关闭连接
        this.ws.onclose = (e) => {
            console.log('onclose',e)
            this._closeSocket(e)
        }
        // 报错
        this.onerror = (e) => {
            console.log('onerror',e)
            this._closeSocket(e)
        }
    }
    sendMsg(data) {
        let msg = JSON.stringify(data)
        return this.ws.send(msg)
    }
    _resetHeart() {
        clearInterval(this.pingInterval)
        return this
    }
    _heartCheck() {
        this.pingInterval = setInterval(() => {
            if(this.ws.readyState === 1) {
                this.ws.send(JSON.stringify({type: 'ping'}))
            }
        },this._timeout)
    }
    _closeSocket(e) {
        this._resetHeart()
        if(this.status !== 'close') {
            console.log('断开,重连', e)
            if(this.isReconnection){
                // 重连
                this.connect()
            }
        }else{
            console.log('手动关闭了', e)
        }
    }
    close() {
        this.status = 'close'
        this._resetHeart()
        return this.ws.close();
    }
}

页面调用

// 引入文件
<script src="./socket.js"></script>

<script>
    // 初始化
    const ws = new Socket({
        url: 'wss://www.example/websocket',
        name: '',			// name
        isHeart:true,			// 是否心跳
        isReconnection:false,		// 是否断开重连
        received: function(data){
        	// 监听服务器返回信息
            console.log("received",data)
        }
    });
    // 建立连接
    let data = {
        type: 'init'
    }
    ws.connect(data);
    
    // 发送消息
    let sendData = {
       type: 'sendMsg'
    }
    ws.sendMsg(sendData)
    
    // 手动关闭
    ws.close()
</script>
posted @ 2023-02-11 22:43  wanglei1900  阅读(138)  评论(0编辑  收藏  举报