这篇文章之前,很多人听说过WebSocket,没见过,也没用过。他们认为这是一项非常高的技术,但实际上这种技术并不神秘。可以说是一门容易掌握的技术。希望大家看完这篇文章,把文中的栗子拿出来亲自尝试一下,做到实践出真知。学习游泳健身:博客,前端积累的文档,公众号,GitHubWebSocket解决什么问题:客户端(浏览器)与服务端通信,只能由客户端发起ajax请求进行通信,并且服务器不能主动向客户端推送信息。当出现体育赛事、聊天室、实时位置等场景时,客户端只能通过轮询(定时请求)的方式获取服务端的变化,以了解服务端是否有新的信息变化。轮询效率低,资源浪费(需要不断发送请求,不断连接服务器)。WebSocket的出现使得服务端可以主动向客户端发送信息,使得浏览器能够进行实时的双向通信。这就是WebSocket解决的问题举个超简单的栗子:新建一个html文件,在某处运行这个栗子试试,就可以轻松上手WebSocket:functionsocketConnect(url){//将客户端连接到服务器letws=newWebSocket(url);//返回`WebSocket`对象并赋值给变量ws//连接成功回调ws.onopen=e=>{console.log('连接成功',e)ws.send('我发送了一个给服务器的消息');//客户端与服务器通信}//监听服务器返回的信息ws.onmessage=e=>{console.log('Theserverreturns:',e.data)//dosomething}returnws;//返回websocket对象}letwsValue=socketConnect('ws://121.40.165.18:8800');//websocket对象上面栗子中WebSocket的接口地址来自:WebSocket在线测试,开发时也可以用来测试后台给的地址是否可用。WebSocket类:当项目中很多地方使用到WebSocket时,将其封装成一个类是更好的选择。下面的栗子已经评论的很详细了。您也可以创建一个html文件并直接使用它。websocket常用的API都包含了。下面注释的代码,不用管,涉及到心跳机制,用于维护WebSocket连接的类WebSocketClass{/***@description:初始化实例属性,保存参数*@param{String}urlwsinterface*@param{Function}msgCallback服务器信息回调,向函数传递数据*@param{String}name可选值,用于区分ws,用于debugger*/constructor(url,msgCallback,name='default'){this.网址=网址;this.msgCallback=msgCallback;this.name=名称;这个.ws=null;//websocket对象this.status=null;//websocket是否关闭}/***@description:初始化websocket连接或重新连接webSocket时调用*@param{*}可选传递的数据*/connect(data){//创建一个新的WebSocket实例this.ws=新的WebSocket(this.url);this.ws.onopen=e=>{//连接ws成功回调this.status='open';console.log(`${this.name}连接成功`,e)//this.heartCheck();if(data!==undefined){//somethingtopass数据将被发送到后端returnthis.ws.send(data);}}//监听服务器服务器端返回的信息this.ws.onmessage=e=>{//将数据传递给回调函数并执行回调//if(e.data==='pong'){//this.pingPong='乒乓球';//服务器返回pong,修改pingPong的状态//}returnthis.msgCallback(e.data);}//ws关闭回调this.ws.onclose=e=>{this.closeHandle(e);//判断是否关闭}//ws错误回调this.onerror=e=>{this.closeHandle(e);//判断是否关闭}}//heartCheck(){////心跳机制的时间可以和自己比较后端协议//this.pingPong='ping';//ws心跳机制状态值//this.pingInterval=setInterval(()=>{//if(this.ws.readyState===1){////在发送之前检查ws是否连接//this.ws.send('ping');//客户端发送ping//}//},10000)//this.pongInterval=setInterval(()=>{//this.pingPong=false;//if(this.pingPong==='ping'){//this.closeHandle('pingPong没有更改为pong');//没有返回pongrestartwebSocket//}////重置为ping如果下一次ping发送失败或pong返回失败(pingPong不会更改为pong),它将重新启动//console.log('returnpong')//this.pingPong='ping'//},20000)//}//向服务器发送信息sendHandle(data){console.log(`${this.name}向服务器发送消息:`,data)returnthis.ws.send(data);}closeHandle(e='err'){//因为webSocket不稳定,规定只能手动关闭(调用closeMyself方法),否则重新连接if(this.status!=='close'){console.log(`${this.name}断开连接,重新连接websocket`,e)//if(this.pingInterval!==undefined&&this.pongInterval!==undefined){////清除定时器//clearInterval(this.pingInterval);//clearInterval(this.pongInterval);//}this.connect();//重新连接}else{console.log(`${this.name}websocketismanuallyclosed`)}}//手动关闭WebSocketcloseMyself(){console.log(`close${this.name}`)this.status='close';返回this.ws.close();}}functionsomeFn(data){console.log('接收服务器消息的回调:',data);}//constwsValue=newWebSocketClass('ws://121.40.165.18:8800',someFn,'wsName');//这个链接一天只能发50次消息constwsValue=newWebSocketClass('wss://echo.websocket.org',someFn,'wsName');//阮一峰老师教程链接wsValue.connect('立即与服务器通信');//连接到服务器//setTimeout(()=>{//wsValue.sendHandle('发送消息到服务器')//},1000);//setTimeout(()=>{//wsValue.closeMyself();//closews//},10000)我直接在栗子里一起写的,你可以把class放在一个js文件里,export出来,然后在需要用到的地方import进去,通过使用它的参数。WebSocket不稳定。WebSocket不稳定。使用一段时间后,它可能会断开连接。至于为什么会断开连接,目前好像还没有什么舆论,所以我们需要保持WebSocket的连接状态。这里推荐两种方法。WebSocket设置变量判断是否手动关闭连接:这个是class类中使用的方法:设置一个变量,在webSocket关闭/错误回调中,判断是否手动关闭,如果没有,则重新连接,如此操作优缺点如下:优点:请求少(相对于心跳连接),易于设置。缺点:可能会导致数据丢失。在断开和重连期间,双方恰好在通信。WebSocket心跳机制:由于第一种方案的不足,可能还有一些其他未知情况导致连接断开,没有触发Error或Close事件。这样,实际的连接已经断开了,但是客户端和服务端不知道,还在等待消息。于是聪明的程序员想出了一个解决方案,叫做心跳机制:客户端像心跳一样每隔固定的时间发送一个ping告诉服务器我还活着,服务器会返回pong告诉客户端,服务器还活着。具体实现方法在上面类的注释中打开,可以看到效果。关于WebSocket,我怕一开始文字堆砌太多,吓到你了。既然大家都知道怎么用了,那我们回头看看WebSocket的其他知识点。WebSocket的当前状态:WebSocket.readyState下面是WebSocket.readyState的四个值(四种状态):0:表示正在连接1:表示连接成功,可以通信2:表示连接isclosing3:表示连接已经关闭,或者连接打开失败,我们可以利用当前状态做一些事情。比如上面的栗子,只有WebSocket连接成功后才允许客户端发送ping。if(this.ws.readyState===1){//在发送之前检查ws是否处于链接状态this.ws.send('ping');//clientsendsping}WebSocket也可以在这里发送/接收二进制数据我也没试过。看了阮一峰老师的WebSocket教程才知道还有这么个东西。大家有兴趣可以再去google一下,大家就可以知道了。二进制数据包括:blob对象和Arraybuffer对象,所以我们需要分别处理。//接收数据ws.onmessage=function(event){if(event.datainstanceofArrayBuffer){//判断ArrayBuffer对象}if(event.datainstanceofBlob){//判断Blob对象}}//发送Blob对象Exampleletfile=document.querySelector('input[type="file"]').files[0];ws.send(file);//发送ArrayBuffer对象的例子varimg=canvas_context.getImageData(0,0,400,320);varbinary=newUint8Array(img.data.length);for(vari=0;i
