简易web socket封装
2020/6/23 11:27:02
本文主要是介绍简易web socket封装,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
序
游手好闲,无所事事,危机四伏...轮子虽然多,不是自己的用起来总感觉怪怪的;自己造的,即使七歪八拐,也挺‘快乐’的(^o^)。
socket功能点
大部分我们使用socket的主要功能就是消息实时同步及心跳机制,自己弄之前网上找了一下,发现有挺多好用的socket封装库了,例如vue的vue-socket.io等。
socket 简易封装
- socket具有各种状态(CLOSED,CLOSING,OPEN,CONNECTING)
- 网站协议不同,socket的协议也不同(http://->ws://,https://->wss://)
- 设置socket重连机制(基础重连时间及重连时间策略)
- 设置socket心跳时间(保活机制)
- 设置socket各种回调事件(onopen,onerror,onmessage,onclose,onreconnect)
- 消息数据适配
兼容性及协议判断
websocket在IE10以下不支持,既然能用上socket其实我们已经不在乎这个了-.-,但是为了防止意外出错,做个简单的判断还是挺ok的:/** * 判断是否支持websocket */ static isSupport() { return /\{\s*\[native code\]\s*\}/.test(WebSocket.toString()) } 复制代码
这里默认根据当前的网站协议判断ws的协议
window.location.protocol.toLowerCase() === 'https:'
,然后根据协议及参数进行url的拼装:
/** * 获取websocket完整链接 * @param {string} url 基础websocket URI */ static getUrl(url, params = {}, isHttps = false) { let _path = [url] let _prefix = 'ws://' if (isHttps) { _prefix = 'wss://' } _path.unshift(_prefix) let _query = [] for (let key in params) { _query.push(`${key}=${params[key]}`) } if (url.indexOf('?') > -1) { _path.push('&') } else { _path.push('?') } _path.push(_query.join('&')) return _path.join('') } 复制代码
配置参数
根据上面功能点,初略的得出socket的基本参数配置:
<!--默认策略--> const reconnectStrategy = (count, base) => { return base * count } static defaultConfig = { // 是否时https连接(默认判断当前URL协议) isHttps: window.location.protocol.toLowerCase() === 'https:', // 重连时间step(为0表示不重连) reconnectTime: 6 * 1000, // 重连时间策略 reconnectStrategy, // 心跳时间间隔 heartBeatTime: 60 * 1000, // 服务器数据适配 serveAdapt: loop, // onopen回调 onopen: loop, // onerror回调 onerror: loop, // onmessage回调 onmessage: loop, // onclose回调 onclose: loop, // onreconnect回调 onreconnect: loop, } 复制代码
执行(使用)
使用类的写法,这里直接实例化类就可以返回当前ws的实例对象;
loopCount
表示此次重连的次数,重连成功后重置,eventPoll
为简单的事件池用于注册一些简易的事件回调。
constructor(url, params = {}, config = {}) { // 事件注册池 this.eventPoll = {} // websocket初始化参数 this.params = params // 配置项 this.config = { ...WS.defaultConfig, ...config } this.url = WS.getUrl(url, this.params, this.config.isHttps) this.loopCount = 0 this._init() __log( '%c--> WEB SOCKET <--', `text-shadow: 0 1px 0 #ccc,0 2px 0 #c9c9c9, 0 3px 0 #bbb,0 4px 0 #b9b9b9, 0 5px 0 #aaa,0 6px 1px rgba(0,0,0,.1), 0 0 5px rgba(0,0,0,.1),0 1px 3px rgba(0,0,0,.3), 0 3px 5px rgba(0,0,0,.2),0 5px 10px rgba(0,0,0,.25), 0 10px 10px rgba(0,0,0,.2),0 20px 20px rgba(0,0,0,.15); font-size:5em;color: transparent; ` ) } 复制代码
实例化后,就会执行ws的_init
方法,进行ws的创建及回调的监听,ws链接成功后通知用户并开启保活_onHeartBeat
,在保活阶段有个小坑(要实时判断当前ws是否属于OPEN状态,否则手动触发_onError
事件。):
/** * 初始化(注册回调) */ async _init() { if (WS.isSupport()) { this.ws = new WebSocket(this.url) this.ws.onopen = this._onOpen.bind(this) this.ws.onmessage = this._onMessage.bind(this) this.ws.onclose = this._onClose.bind(this) this.ws.onerror = this._onError.bind(this) } else { log('Your Browser is not support WebSocket.') } } /** * 心跳函数,状态不为OPEN时触发重连机制(必须在ws open后调用该函数) */ async _onHeartBeat() { await this._sleep(this.config.heartBeatTime) if (this.ws.readyState === STATE.OPEN) { log('>>>>ping>>>>') this.ws.send('ping') this._onHeartBeat() } else { this._onError(new Event('ws_close')) } } /** * ws开启回调 */ _onOpen() { log('websocket已打开,开启心跳') this.loopCount = 0 this.config.onopen(this.ws) this._onHeartBeat() } /** * 消息监听回调 * @param {Object} msg 服务器推送消息 */ _onMessage(msg) { try { let _data = JSON.parse(msg.data) this.config.onmessage(_data) let _events = [] // code码事件 let _code = Number(_data.code) let _codeEvents = this.eventPoll[_code] if (_codeEvents) { _events.push(..._codeEvents) } for (let _cb of _events) { _cb(_data) } } catch (error) { log('数据解析错误', error) } } /** * ws关闭回调 */ _onClose(e) { log(`>>>>Socket已关闭>>>>`) this.config.onclose(e) } /** * ws出错回调 */ _onError(e) { this.config.onerror(e) if (!this.config.reconnectTime) { return } let _delayTime = this.config.reconnectStrategy(this.loopCount++, this.config.reconnectTime) log(`>>>>${_delayTime / 1000}s后尝试重新连接>>>>`) this.config.onreconnect(_delayTime, this.loopCount) this._sleep(_delayTime).then(() => { log(`>>>>第${this.loopCount}次重新连接>>>>`) this._init() }) } 复制代码
- 在
_onError
回调中,判断reconnectTime
是否大于0(0表示不会重连),然后根据当前轮次及重连基础时间得出当前过多久开始重连_delayTime
(重连就是简单的重新初始化ws-即重新调用_init
) 以下是eventPoll的简易实现(类型及回调):
/** * 订阅消息回调 * @param {Any} type 事件码 * @param {Function} cb 事件回调 * @returns {Function} 取消订阅事件 */ subscribe(type, cb) { let _poll = this.eventPoll[type] || (this.eventPoll[type] = []) let _index = _poll.indexOf(cb) if (_index === -1) { _poll.push(cb) _index = _poll.length - 1 } return () => { let _i=_poll.findIndex(p=>p===cb) if(_i>-1){ _poll.splice(_i, 1) return true } return false } } 复制代码
- 最后在退出当前页面时要销毁当前ws(这里设置
reconnectTime
为0是为了关闭_onError
中不断重连的机制判断):
/** * 关闭重连,销毁 */ closeReconnect() { this.config.reconnectTime = 0 this.ws.close() } 复制代码
安装使用
npm使用:
npm i '@qiangkun/sdk' import { Easysocket } from '@qiangkun/sdk' const EVENT_MAP = { 201: 'sw_eva_pack_update', 202: 'sw_eva_video_update', 203: 'sw_eva_nego_update', 204: 'sw_eva_wait_update', '5563': 'token_expire', '3002': 'login_other' } let sw = new Easysocket( 'xxx', { app_id: 'app_agency', access_token: getToken() }, { onerror: (e) => { console.log('error',e) }, onclose: () => { console.log('close') } } ) for (let code in EVENT_MAP) { sw.subscribe(code, msg => { <!--使用vue事件机制--> bus.$emit(EVENT_MAP[code], msg) }) } 复制代码
结语
自己倒腾,锅自己认,自己背--
不间断的拨弄原生js其实也算是对自己能力提升有所帮助,拨弄不了后面反正还有一堆的库使用,这样边学习边积累还是不错的~_~。
这篇关于简易web socket封装的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-09-28pyqt 怎么打包整个项目-icode9专业技术文章分享
- 2024-09-28laravel Commands 创建带有参数的 Artisan 命令的步骤和示例-icode9专业技术文章分享
- 2024-09-28antd怎么实现渲染tiff图片-icode9专业技术文章分享
- 2024-09-28英文半角中划线和中文全角的中划线有什么区别-icode9专业技术文章分享
- 2024-09-28nvm npm 和node 他们之间有什么关系-icode9专业技术文章分享
- 2024-09-28Node Version Manager (nvm)使用教程-icode9专业技术文章分享
- 2024-09-28nvm命令太慢,是什么原因-icode9专业技术文章分享
- 2024-09-28Kotlin 如何增加、删除和修改 MutableStateFlow 中的值。-icode9专业技术文章分享
- 2024-09-28Kotlin的stateFlow.update 写法介绍-icode9专业技术文章分享
- 2024-09-28kotlin 怎么获取当前时间格式-icode9专业技术文章分享