全国免费咨询:

13245491521

VR图标白色 VR图标黑色
X

中高端软件定制开发服务商

与我们取得联系

13245491521     13245491521

2024-12-29_WebSocket 心得分享

您的位置:首页 >> 新闻 >> 行业资讯

WebSocket 心得分享 点击关注公众号,“技术干货”及时达! 一、前言?本文将介绍 WebSocket 的封装,比如:心跳机制,重连和一些问题如何去处理 ?二、背景之前,钱包相关的查询,我们是使用的轮询方案来做的,后来更新了一次需求,需要做一些实时数据统计的更新,然后顺带给钱包的余额也用长连接来做了,好,那么故事就开始了... 某天, 「老板:」 我钱怎么没了,但是我这里查账户还有。 「我的内心:」 恩?这玩意难道说... 后端没返? 和后端沟通以后,感觉是返回了的,被挤账号了?排查了一段时间以后,最终我将问题锁定在手机息屏的操作上。 因为我们是一个 「H5」 的项目,APP 是嵌套在 webview 中,所以不能操作原生的事件来处理,只能将方案控制在浏览器提供的事件来处理。 好了,接下来各位可以看我是如何处理这个问题,如果没有搞过也是可以有不少收获,也欢迎大神评论区交流其他方案。 三、WebSocket3.1 什么是 WebSocket ?为什么使用他?以下是百度百科中对 「WebSocket」 的定义: WebSocket 是一种在单个 TCP 连接上进行 全双工 通信的协议。WebSocket 通信协议于2011年被 IETF 定为标准 RFC 6455,并由 RFC7936 补充规范。WebSocket API 也被 W3C 定为标准。 WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。 「WebSocket 的关键特点」 「双向通信(Full Duplex)」 客户端和服务器都可以主动发送数据,而不是像 HTTP 一样只能由客户端发起请求。「实时性」 消息可以实时传递,延迟更低,适合需要实时更新的场景。「持久化连接」 使用单个 TCP 连接完成多次数据交互,无需为每次通信重新建立连接。「轻量级协议」 WebSocket 头部信息非常小,比传统 HTTP 请求的头部要轻量。「节约资源」 长连接减少了资源消耗,特别是在频繁通信的场景中。上述中,是 AI 给我们总结的 WebSocket 的特点,接下来我们要知道我们为什么使用他,HTTP 他能不能做,他的局限性又在哪里? 「传统 HTTP 的局限性:」 HTTP 是基于请求-响应模型的,客户端必须发起请求,服务器才能返回数据。如果需要实时更新(如股票价格、在线聊天),通常需要使用轮询(Polling)或长轮询(Long Polling),这会导致:高资源消耗(频繁的连接建立和断开)。高网络流量(每次请求都包含冗长的 HTTP 头部信息)。更高的延迟(数据可能需要等待较长时间才能返回)。其实 HTTP 是可以实现的,如果 HTTP 请求频繁三次握手和四次挥手的操作会占用大量资源,HTTP/1.1 以后开启了 「Keep-Alive (长连接)」,可以复用连接,但是实时的情况下,响应模型仍然会导致较高的延迟和资源消耗。 相比之下,WebSocket 通过一次握手建立连接以后,就可以保持双向通信,服务器可以主动推送数据,无需客户端轮询。解决了 HTTP 带来的一些痛点。 四、封装 WebSocket我们将实现以下几个功能点: 「重连」「心跳机制」「事件回调」「连接状态管理」「销毁」4.1 Javascript 版本classReSocket{ constructor(url,options={}){ this.url=//WebSocket服务器地址 this.options=options;//可选参数 this.socket=null;//WebSocket实例 this.maxReconnectTimes=options.maxReconnectTimes||5;//最大重连次数 this.reconnectTimes=0;//当前重连次数 this.reconnectInterval=options.reconnectInterval||3000;//重连间隔时间(毫秒) this.isClosed=false;//是否已关闭 this.isOpen=false;//是否已打开 this.isConnect=false;//是否已连接 this.isReconnecting=false;//是否正在重连 this.isDestroyed=false;//是否已销毁 this.reconnectTimer=null;//重连定时器 this.heartbeatTimer=null;//心跳定时器 this.heartbeatInterval=options.heartbeatInterval||30000;//心跳间隔时间(默认30秒) this.heartbeatData=options.heartbeatData||"ping";//心跳数据 this.onMessageCallback=null;//消息接收回调 this.onOpenCallback=null;//连接成功回调 this.onCloseCallback=null;//连接关闭回调 } //创建WebSocket实例 createSocket(){ this.socket=newWebSocket(this.url); this.socket.onopen=()={ this.isOpen=true; this.isConnect=true; this.reconnectTimes=0;//重连次数归零 this.startHeartbeat();//启动心跳机制 if(this.onOpenCallback)this.onOpenCallback();//调用连接成功回调 this.socket.onmessage=event={ if(this.onMessageCallback)this.onMessageCallback(event.data);//调用消息接收回调 this.socket.onclose=()={ this.isOpen=false; this.isConnect=false; this.stopHeartbeat();//停止心跳机制 if(this.onCloseCallback)this.onCloseCallback();//调用连接关闭回调 if(!this.isClosedthis.reconnectTimesthis.maxReconnectTimes){ this.reconnect();//尝试重连 } this.socket.onerror=error={ console.error("WebSocket错误:",error);//错误处理 } //开始连接 connect(){ if(this.isDestroyed)return;//如果已销毁,则不再连接 this.createSocket();//创建WebSocket实例 } //重连 reconnect(){ if(this.isReconnecting||this.reconnectTimes=this.maxReconnectTimes) return;//防止重复重连 this.isReconnecting=true; this.reconnectTimes++;//增加重连次数 this.reconnectTimer=setTimeout(()={ console.log(`正在重连...(${this.reconnectTimes})`);//打印重连次数 this.createSocket();//再次创建WebSocket实例 this.isReconnecting=false;//重连状态设置为false },this.reconnectInterval);//按设定时间重连 } //发送消息 send(data){ if(this.isOpen){ this.socket.send(data);//发送数据 }else{ console.error("WebSocket未打开,无法发送消息。");//提示错误 } } //设置消息接收回调 onMessage(callback){ this.onMessageCallback=callback;//绑定接收消息的回调 } //设置连接成功回调 onOpen(callback){ this.onOpenCallback=callback;//绑定连接成功的回调 } //设置连接关闭回调 onClose(callback){ this.onCloseCallback=callback;//绑定连接关闭的回调 } //启动心跳机制 startHeartbeat(){ this.heartbeatTimer=setInterval(()={ if(this.isOpen){ this.send(this.heartbeatData);//发送心跳数据 } },this.heartbeatInterval);//按设定的时间间隔发送 } //停止心跳机制 stopHeartbeat(){ if(this.heartbeatTimer){ clearInterval(this.heartbeatTimer);//清除心跳定时器 this.heartbeatTimer=null; } } //关闭连接 close(){ this.isClosed=true;//设置为已关闭 this.isOpen=false; this.socket.close();//关闭WebSocket连接 this.stopHeartbeat();//停止心跳机制 clearTimeout(this.reconnectTimer);//清除重连定时器 } //销毁实例 destroy(){ this.isDestroyed=true;//设置为已销毁 this.close();//关闭连接 } } 4.2 Typescript 版本typeReSocketOptions={ maxReconnectTimes?:number;//最大重连次数 reconnectInterval?:number;//重连间隔时间(毫秒) heartbeatInterval?:number;//心跳间隔时间(毫秒) heartbeatData?:string;//心跳数据 }; classReSocket{ privateurl:string; privatesocket:WebSocket|null=null; privatemaxReconnectTimes:number; privatereconnectTimes:number=0; privatereconnectInterval:number; privateisClosed:boolean=false; privateisOpen:boolean=false; privateisConnect:boolean=false; privateisReconnecting:boolean=false; privateisDestroyed:boolean=false; privatereconnectTimer:NodeJS.Timeout|null=null; privateheartbeatTimer:NodeJS.Timeout|null=null; privateheartbeatInterval:number; privateheartbeatData:string; privateonMessageCallback:((message:string)=void)|null=null; privateonOpenCallback:(()=void)|null=null; privateonCloseCallback:(()=void)|null=null; constructor(url:string,options:ReSocketOptions={}){ this.url=url; this.maxReconnectTimes=options.maxReconnectTimes|| this.reconnectInterval=options.reconnectInterval||3000; this.heartbeatInterval=options.heartbeatInterval||30000; this.heartbeatData=options.heartbeatData||'ping'; } privatecreateSocket():void{ this.socket=newWebSocket(this.url); this.socket.onopen=()={ this.isOpen=true; this.isConnect=true; this.reconnectTimes=0; this.startHeartbeat(); if(this.onOpenCallback)this.onOpenCallback(); this.socket.onmessage=(event:MessageEvent)={ if(this.onMessageCallback)this.onMessageCallback(event.data); this.socket.onclose=()={ this.isOpen=false; this.isConnect=false; this.stopHeartbeat(); if(this.onCloseCallback)this.onCloseCallback(); if(!this.isClosedthis.reconnectTimesthis.maxReconnectTimes){ this.reconnect(); } this.socket.onerror=(error:Event)={ console.error("WebSocket错误:",error); } publicconnect():void{ if(this.isDestroyed)return; this.createSocket(); } privatereconnect():void{ if(this.isReconnecting||this.reconnectTimes=this.maxReconnectTimes)return; this.isReconnecting=true; this.reconnectTimes++; this.reconnectTimer=setTimeout(()={ console.log(`正在重连...(${this.reconnectTimes})`); this.createSocket(); this.isReconnecting=false; },this.reconnectInterval); } publicsend(data:string):void{ if(this.isOpenthis.socket){ this.socket.send(data); }else{ console.error("WebSocket未打开,无法发送消息。"); } } publiconMessage(callback:(message:string)=void):void{ this.onMessageCallback=callback; } publiconOpen(callback:()=void):void{ this.onOpenCallback=callback; } publiconClose(callback:()=void):void{ this.onCloseCallback=callback; } privatestartHeartbeat():void{ this.heartbeatTimer=setInterval(()={ if(this.isOpenthis.socket){ this.send(this.heartbeatData); } },this.heartbeatInterval); } privatestopHeartbeat():void{ if(this.heartbeatTimer){ clearInterval(this.heartbeatTimer); this.heartbeatTimer=null; } } publicclose():void{ this.isClosed=true; this.isOpen=false; if(this.socket){ this.socket.close(); } this.stopHeartbeat(); if(this.reconnectTimer){ clearTimeout(this.reconnectTimer); } } publicdestroy():void{ this.isDestroyed=true; this.close(); } } export{ReSocket 4.3 如何使用?首先简单写个 ws 的服务,我的 「Node」 环境是 20.18.0 创建一个 Socket 的文件夹 vscode 打开执行: npminit-y 生成完毕 package.json 之后,我们安装 ws : npmiws 创建 app.js 写一个简单服务 : constWebSocket=require("ws"); //创建WebSocket服务器,监听端口8080 constwss=newWebSocket.Server({port:8080 //监听客户端连接 wss.on("connection",(ws)={ console.log("客户端已连接"); //监听客户端发送的消息 ws.on("message",(message)={ console.log("收到客户端消息:",message); //向客户端发送回复 ws.send(`服务器回复:${message}`); //发送一条欢迎消息给客户端 ws.send("欢迎连接WebSocket服务器"); }); //打印服务器地址 console.log("WebSocket服务器已启动:ws://localhost:8080"); 执行运行命令 : node.\app.js 这里可以先用一个 WebSocket 调试工具试试是否创建成功 这里我是用的是 WebSocket在线测试工具 ,效果如下图: 看到欢迎连接的时候,说明我们这个服务已经成功启动了,接下来就是 Javascript 中如何使用了,创建一个 index.html ,然后引入我们封装好的 !DOCTYPEhtml htmllang="en" head metacharset="UTF-8" metaname="viewport"content="width=device-width,initial-scale=1.0" titlews调试/title /head scriptsrc="./socket.js"/script script varws=newReSocket('ws://localhost:8080'); ws.connect() ws.onMessage((res)={ console.log('onMessage',res) }) /script body /body /html 打开浏览器之后在控制台中看日志,如图: 在网络中我们需要在这里看: 到这里,如果你跟着做了一遍,你已经掌握了,如果感觉现在没时间,可以收藏,点赞,留个标记,毕竟收藏等于学会了?????? 五、我的痛点如何处理其实我的封装对于很多浏览器都是可以跑的,如果你复制去跑不了,那就人跑,明白我的意思吧?好了,其实这个封装,没有一些特殊兼容,比如: 浏览器兼容性,某些浏览器支持不完整,可能就要降级处理了,具体某些说的是哪个浏览器,大家心里都知道代理和网络环境问题,某些企业网络或代理服务器会拦截或限制 WebSocket 流量网络状态检测,在网络断开但没有触发 onclose 或 onerror 时,可能无法及时重新连接心跳包超时检测,如果服务器没有正确响应心跳包,客户端可能无法及时发现连接异常大数据传输和分片处理,发送大数据包可能导致超时或失败浏览器生命周期事件,在浏览器后台或移动设备息屏时,WebSocket 可能被挂起或断开??? 我的问题就是在这里等等... 所以,需要各位根据自己使用场景,简单的需求基本上还是可以用的,如果场景涵盖比较多,这时候就可以优先考虑三方库使用 「浏览器生命周期事件:」 当我在移动设备息屏时,我的 WebSocket 确实不会触发心跳,然后后端就给我挂了,那么我们浏览器其实提供了一个 visibilitychange 给我们使用,可以这样写: //页面可见性监听 document.addEventListener("visibilitychange",()={ if(document.visibilityState==="visible"){ console.log("页面可见,尝试恢复WebSocket连接..."); if(!socket.isConnect){ socket.connect();//页面可见时尝试恢复连接 } }else{ console.log("页面隐藏,关闭WebSocket连接..."); socket.close();//页面隐藏时关闭连接以节省资源 } }); 其实,这个我感觉还不太满足我,所以我又添加了一个定时任务来执行检验,代码如下: //页面可见性监听 document.addEventListener("visibilitychange",()={ if(document.visibilityState==="visible"){ console.log("页面可见,尝试恢复WebSocket连接..."); if(!socket.isConnect){ socket.connect(); } lastActiveTime=Date.now();//更新最近活动时间 }else{ console.log("页面隐藏,关闭WebSocket连接..."); socket.close(); } }); //定时任务-检测WebSocket状态及页面活跃度 conststartCheckInterval=()={ checkInterval=setInterval(()={ constnow=Date.now(); //检测WebSocket是否断开,尝试重连 if(!socket.isConnect){ console.log("WebSocket未连接,尝试重连..."); socket.connect(); } //检测最近活动时间,判断页面是否处于非活跃状态 if(now-lastActiveTime10000){//超过10秒未活动 console.log("检测到页面可能处于非活跃状态!"); //此处可执行其他恢复或提醒操作 } },5000);//每5秒检查一次 }; //清理定时任务 constclearCheckInterval=()={ if(checkInterval){ clearInterval(checkInterval); checkInterval=null; } }; //初始化定时任务 startCheckInterval(); //页面销毁的时候调用clearCheckInterval清理 结语很久没有更新了,狠狠的写了3000多字,希望这篇文章还是对读者们有帮助。最近也是经历了裁员,和找工作一系列的事情,小小吐槽以下,就业环境不容乐观,但是基本上看见这篇文章的读者,都是热爱技术的,多学点知识基本上储备量上去了,面试还是很容易通过的。 「最后,看到此刻的你,祝你工作顺利,生活愉快!」 点击关注公众号,“技术干货”及时达! 阅读原文

上一篇:2023-08-16_【招聘】位于上海的 W、bangX、HardCandy 硬糖 等你来撩 下一篇:2019-09-09_「转」【户外广告观察】赛麟汽车首投电梯电视 位列品牌榜单第二

TAG标签:

21
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设网站改版域名注册主机空间手机网站建设网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。
项目经理在线

相关阅读 更多>>

猜您喜欢更多>>

我们已经准备好了,你呢?
2022我们与您携手共赢,为您的企业营销保驾护航!

不达标就退款

高性价比建站

免费网站代备案

1对1原创设计服务

7×24小时售后支持

 

全国免费咨询:

13245491521

业务咨询:13245491521 / 13245491521

节假值班:13245491521()

联系地址:

Copyright © 2019-2025      ICP备案:沪ICP备19027192号-6 法律顾问:律师XXX支持

在线
客服

技术在线服务时间:9:00-20:00

在网站开发,您对接的直接是技术员,而非客服传话!

电话
咨询

13245491521
7*24小时客服热线

13245491521
项目经理手机

微信
咨询

加微信获取报价