websocket

websocket

websocket是一种协议,用于提供低延迟,全双工,和长期运行的连接。
全双工是指通信的两个参与方可以同时发送和接收数据,不需要等待对方的响应或传输完成,通过建立全双工的长时间连接,客户端和服务器之间就能实现高效、实时性更强的通信。

WebSocket之前的解决方法:

用的轮询长轮询,缺点会产生大量的请求和响应,造成不必要的网络开销和延迟

webSocket应用场景:低延迟实时连接的应用

webSocket优点:

双向实时通信,降低延迟,可以减少请求和响应的开销,因为它的连接只需要建立一次,
它允许服务器和客户端之间通过单个TCP连接进行双工通信并且进行实时的数据交换

建立连接过程:

客户端发送一个HTTP常规get请求给服务器,请求头中带上Upgrade告诉服务器升级为WebSocket协议。
服务器接受请求,并返回一个表示成功的响应。
客户端和服务器之间建立WebSocket连接,可以随时进行双向通信。
在这里插入图片描述

websocket使用

// WebSocket构造函数,创建WebSocket对象
let ws = new WebSocket('ws://localhost:8888')

// 连接成功后的回调函数
ws.onopen = function (params) {
  console.log('客户端连接成功')
  // 向服务器发送消息
  ws.send('hello')
};

// 从服务器接受到信息时的回调函数
ws.onmessage = function (e) {
  console.log('收到服务器响应', e.data)
};

// 连接关闭后的回调函数
ws.onclose = function(evt) {
  console.log("关闭客户端连接");
};

// 连接失败后的回调函数
ws.onerror = function (evt) {
  console.log("连接失败了");
};


// 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,这样服务端会抛异常。
window.onbeforeunload = function() {
    ws.close();
}  

webSocket心跳机制:

1、为什么使用心跳机制为了保持WebSoket稳定的长连接,在建立连接之后,连接状态可能会因各种状态出现异常,比如因为长时间没有数据传输而被切断,服务端和客户端之间通过心跳包保持连接状态。

2、什么是心跳包:心跳包是一种特殊的数据包,它不包含任何实际数据,只是用来维持连接状态的

怎么使用:通常情况下心跳包由客户端和服务端定期发送一个空的数据帧,以确保双方的链接仍然有效,避免链接因为长时间没有数据传输而被中断,如果长时间没有收到对方的心跳包就可以认为连接已经断开需要重新建立连接。

webSoket限制:

不提供加密功能,不安全,可以用SSL协议对来webSoket进行加密,确保数据安全,也可以在服务端限制权限设置白名单或者黑名单只允许特定地址或者域名的客户端进行链接。

浏览器的限制:不支持IE10以前的版本

消耗服务器资源

websocket使用,含心跳重连

//创建utils/webSocket文件
class Socket {
    ws = null
    wsUrl = ''
    status = ''
    reconnectTimes = 0
    needReconnect = true
    /**
     * 
     * @param {Object} config
     * @param {String} config.url               ws链接
     * @param {Boolean} config.needReconnect    是否需要重连
     * @param {Number} config.reconnectTimes    重连次数
     */
    constructor(config = {}) {
        const {url, reconnectTimes, needReconnect} = config
        // console.log('config', config);
        this.wsUrl = url
        this.reconnectTimes = reconnectTimes || 0
        this.needReconnect = needReconnect
        this.init()
    }
    init() {
        if ('WebSocket' in window) {
            // 实例化
            this.ws = new WebSocket(this.wsUrl)
            // 监听事件
            this.ws.onopen = () => this._onOpenFn()
            // 监听错误信息
            this.ws.onerror = err => this._onErrorFn(err)
            // 监听消息
            this.ws.onmessage = msg => this._onMessageFn(msg)
            
            this.ws.onclose = () => this._onCloseFn()

        } else {
            console.log('你的浏览器不支持 WebSocket')
        }
    }
    _onOpenFn(){
        this.status = 'open'
        this.onopen && this.onopen()
    }

    _onErrorFn (err) {
        this.status = 'error'
        this.onerror && this.onerror(err)
        if(this.needReconnect){
            this.reconnect()
        }else {
            this._connnetFailed(err)
        }
    }
    
    _onMessageFn (msg) {
        
        const obj = JSON.parse(msg.data)
        console.log('onmessage', obj);
        this.onmessage && this.onmessage(obj)
    }
    
    _onCloseFn() {
        this.status = 'closed'
        this.onclose && this.onclose()
        
    }
    // 连接失败
    _connnetFailed() {
        console.log('retry failed');
        this.connnetFailed && this.connnetFailed()
    }
    reconnect() {
        if(this.reconnect.lock) return 
        if(this.reconnectTimes && this.reconnect.times >= this.reconnectTimes) {
            this._connnetFailed()
            return 
        }
        this.reconnect.lock = true
        this.reconnect.times = this.reconnect.times || 0
        setTimeout(() => {
            if(this.status!== 'open'){
                this.init()
                this.reconnect.lock = false
                this.reconnect.times ++
            }
            
        }, 500)
    }
    close () {
        this.ws.close()
    }
    send (data) {
        const dataString = JSON.stringify(data)
        this.ws.send(dataString)
    }
}
export default Socket


//调用
import ws from 'utils/webSocket'
import { generateUUID } from 'utils'
import { busListenOnce, busTrigger } from '@/base/eventBus'
/** export const busListenOnce = (eventName = '', callback = () => { }) => {
    if (!eventName) return
    return getBus().$once(eventName, callback)
} 
export const busTrigger = (eventName = '', opts = {}) => {
    if (!eventName) return
    return getBus().$emit(eventName, opts)
}**/
import { connectEvents } from './handleEvents'

let wsClient = null
let uri = ''
// 拉起客户端
async function connectWithDss() {
    // 连接server获取port
    const port = await connectServer()
    uri = `ws://localhost:${port}`
    // 连接client
    await connectClient(uri)
}

/**
 *
 * 1. 连接Server中间件
 * 2. sendMsg: getPort
 * 3. 接收到端口W
 * 3. 使用端口连接 client
 */
function connectServer() {
    return new Promise((resolve, reject) => {
        const url = `ws://localhost:13000`
        const wsServer = new ws({ url, needReconnect: false, reconnectTimes: 2 })
        wsServer.onopen = () => {
            store.commit('sip/SET_IS_INSTALLED', true)
            getPort(wsServer)
        }
        wsServer.onmessage = (msg) => {
            const port = msg.port
            wsServer.close()
            resolve(port)
        }
        wsServer.onerror = (err) => {
            console.log(err)
            // reject(err)
        }
        wsServer.connnetFailed = (err) => {
            store.commit('sip/SET_IS_INSTALLED', false)
            busTrigger(connectEvents.SERVER_CONNECTED_FAILED, err)
        }
    })
}
function connectClient(url) {
    return new Promise((resolve, reject) => {
        wsClient = new ws({ url })
        wsClient.onopen = () => {
            busTrigger(connectEvents.CLIENT_CONNECTED_SUCCEED)
            resolve()
        }
        wsClient.onmessage = (msg) => {
            // console.log('sipCient msg received :>> ', msg)
    
            if (!msg.sequence) {
                // 客户端插件主动发消息
                busTrigger(msg.method, msg)
            } else {
                // 客户端响应消息
                busTrigger(msg.sequence, msg)
            }
        }

    })
    // const url = `ws://${location}:${port}`
    // wsClient.oncloseCb = () => {
    //     wsClient.reconnect()
    // }
}
function closeClient() {
    console.log('closed');
    wsClient && wsClient.close()
}
function getPort(ws) {
    ws.send({
        method: 'getPort',
        uuid: generateUUID(), //用于标识客户端组件,由前端生成
        sequence: '' //同步码
    })
}

let retryTimes = 0
/**
 * 登录
 * @param {Object} params 话机登录参数
* { "1000","sN66vV0m"},
 * { "1001","bgiT60y0"},
 * { "1002","_jdqs0Vr@NGuCMVT"},
 * { "1003","8nRYK@?hRmj?WSSL"},
 * { "1004","5*Rx*JwXuN@?-~*~"},
 * { "1005","nF?xbw2?_?CP@^^-"},
 * { "1006","~p_~?RK0wsaQqYNO"},
 */
function login(params) {
    if (!retryTimes) {
        retryTimes = 0
    } else if (retryTimes > 5) {
        return
    }
    return new Promise((resolve, reject) => {
        const sequence = `login_${Date.now()}`
        wsClient.send({
            method: 'login',
            param: params,
            sequence //同步码
        })
        busListenOnce(sequence, (msg) => {
            if (msg.errorCode === '0') {
                // Message.success('签入成功')
                retryTimes = 0
                resolve(msg)
            } else {
                retryTimes++
                login()
            }
        })
    })
}

// 登出
function logout () {
    return new Promise((resolve, reject) => {
        const sequence = `logout_${Date.now()}`

        wsClient.send({
            method: 'logout',
            sequence
        })
        busListenOnce(sequence, (msg) => {
            if (msg.errorCode === '0') {
                console.log('签出成功')
                // store.commit('sip/SET_IS_LOGIN', false)

                resolve(msg)
            } else {
                reject(msg)
                // login()
            }
        })
        // listenCommon()
    })
}

// 拨打电话
function makeCall({ phoneNumber }){
    const sequence = `makeCall_${Date.now()}`

    return new Promise((resolve, reject) => {
        wsClient.send({
            method: 'makeCall',
            param: {
                phoneNumber //电话号码
            },
            sequence //同步码
        })
        busListenOnce(sequence, (msg) => {
            if (msg.errorCode === '0') {
                // TODO : 
                store.commit('sip/SET_CALLING_STATE', 3)
                resolve(msg)
            } else {
                reject(msg)
            }
        })
    })
}

// 接听电话
function answerCall(){
    const sequence = `answer_${Date.now()}`

    return new Promise((resolve, reject) => {
        wsClient.send({
            method: 'answer',
            param: {
                callid: '' //目前只支持一路通话,不需要填写该参数
            },
            sequence //同步码
        })
        busListenOnce(sequence, (msg) => {
            if (msg.errorCode === '0') {
                store.commit('sip/SET_CALLING_STATE', 2)

                resolve(msg)
            } else {
                reject(msg)
            }
        })
    })
}

// 挂断电话
function hangUp () {
    const sequence = `hangup_${Date.now()}`

    return new Promise((resolve, reject) => {
        wsClient.send({
            method: 'hangup',
            param: {
                callid: '' //目前只支持一路通话,不需要填写该参数
            },
            sequence //同步码
        })
        busListenOnce(sequence, (msg) => {
            if (msg.errorCode === '0') {
                resolve(msg)
                store.commit('sip/SET_CALLING_STATE', 0)

            } else {
                reject(msg)
            }
        })
    })

}

// 保持通话
function setHold() {
    wsClient.send({
        method: 'setHold',
        param: {
            callid: '' //目前只支持一路通话,不需要填写该参数
        },
        sequence: 'setHold' //同步码
    })
}

// 恢复通话
function reinvite () {
    wsClient.send({
        method: 'reinvite',
        param: {
            callid: '' //目前只支持一路通话,不需要填写该参数
        },
        sequence: 'reinvite' //同步码
    })
}
function getWsClient() {
    return wsClient
}

export { connectWithDss, connectServer, closeClient, login, logout, makeCall, answerCall, hangUp, setHold, reinvite, getWsClient}

  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值