WebSocket

您所在的位置:网站首页 webservice使用什么协议 WebSocket

WebSocket

2023-04-07 15:18| 来源: 网络整理| 查看: 265

学习目标

WebSocket

学习内容

创建一个文件夹,初始化项目

npm init -y

需要用到一个库,是node的支持的一个WebSocket的库(ws)

npm i ws

首先我们得知道WebSocket是什么?它是基于http协议的一个扩展,建立在TCP协议上面的。我们都知道长链接,通俗的说就是有来有回,服务器可以向我们客户端进行广播、以及传送消息,客户端也可以向服务端传送消息,有来有回的,就是双向通讯的,可以用来实现即时通讯的。

优点:

与http协议兼容性好

数据的格式比较轻

发送的数据格式种类多,比如二进制、普通的tag

没有同源策略的限制

注意:我们开启这个服务之后呢,WebSocket会在特定的时间段断开连接,就比如没有行为的时候,之间没有发送信息,没有相互之间的通讯,那么服务器会把这个连接断开。那么此时发送信息肯定是没有用的,服务端是接收不到的,那么服务端发送回来的信息也是接收不到的,那么双向通讯被阻断了。

那么也就是说我们在特定时间之内,比如一分半、两份钟,我们需要发送一次信息来确认我们的连接是否是open 的状态。

服务端代码// 导入 const Websocket = require('ws') // 创建一个server,Websocket中有一个Server的属性,注意:是new了一个Websocket的Server,里面给一个端口 const server = new Websocket.Server({ port:8000 }) // 监听 server.on('connection',handleConnection) // 事件处理函数,里面带有wx的实例,也就是WebSocket的实例 function handleConnection(wx){ console.log('---server is connection---') // 监听关闭事件 wx.on('close',handleClose) // 监听错误事件 wx.on('error',handleError) // 监听来消息的时候 wx.on('message',handleMessage) function handleClose(){ // 这个在服务器打印的 console.log('---server is closed---') // 发送一个消息,让客户端接收到已经关闭了 /** 模式:MESSAGE,有可能不是MESSAGE模式,就比如心跳机制,有可能是HEART_BEAT模式, * 但在用户发送消息的时候,mode应该是MESSAGE * 就是有两种形式:一种是心跳机制,客户端向服务端发送消息,另一种是用户在客户端输入消息发送消息给客户端 */ this.send({ mode:'MESSAGE', msg:'---server is closed---' }) } function handleError(e){ console.log('---server is Error---',e) } /** * 接收一个data,客户端如何发送消息的呢,发送一个对象 * { * mode:'MESSAGE' or 'HEART_BEAT' * msg:string * } */ function handleMessage(data){ const { mode, msg } = JSON.parse(data) switch(mode){ case MESSAGE: // 根据需求处理 console.log('---User message---') this.send(JSON.stringify(JSON.parse(data))) break; case HEART_BEAT: console.log('---HeartBeat message---') this.send(JSON.stringify(JSON.parse(data))) break; default: break; } } }

接下来在package.json中添加

"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev":"nodemon ./app.js" },

用nodemon来监听app.js 。注意:nodemon 要全局安装,如果有报错,请查看环境变量

npm i nodemon -g

启动

客户端

接下来在当前文件中创建一个vue客户端项目。

//App.vue Send import Ws from './Ws' // 实例 let ws = null; // 创建 const init = async ()=>{ // 实例化一个WebSocket的对象,要用await,比如重连肯定是异步的,需要返回一个ws的对象 // create的时候相当于new Ws ws = await Ws.create('ws://localhost:8000') } const sendMsg = ()=>{ if(ws){ // 用户信息, HEART_BEAT封装在Ws中 ws.send(JSON.stringify({ mode:'MESSAGE', msg:'Hello' })) } } init()

下面是Ws.js的代码,但是我们在运行时会看见有一个错误

//Ws.js let heartBeatTimer = null let reconnectingTimer = null const WS_MODE = { MESSAGE:'MESSAGE', HEART_BEAT:'HEART_BEAT' } // 继承WebSocket // 当new Ws的时候,就相当于new一个WebSocket的对象 class Ws extends WebSocket{ // 当new的时候填写的url,相当于在这加一个url,相当于调constructor // url是 ws://localhost:8000 constructor(url){ // 将url交给super,则是WebSocket的constructor super(url) // 我们现在肯定是有两种状态的,第一种是连接状态,一种是未连接状态 this.connentStatue = false; // 当实例化重连的时候,还得需要url,名字不能起this.url,因为WebSocket实例中有url this.wsUrl = url this.init() } // init 相当于一个开关 init(){ this.bindEvent() } // 绑定函数,如open、close、error、message bindEvent(){ // this:Ws的实例,监听open、close、error、message this.addEventListener('open',this.handleOpen,false) this.addEventListener('close',this.handleClose,false) this.addEventListener('error',this.handleError,false) this.addEventListener('message',this.handleMessage,false) } handleOpen(){ console.log('--- Client is connected---') // 连接时,状态肯定为true this.connentStatue = true // 连接时,开启心跳机制,检查是否断开,断开需要重连 // 在startHeartBeat肯定要做间隙interval,则在reconnect中需要做延时进行重连,则需要两个计时器 this.startHeartBeat() } handleClose(){ console.log('--- Client is closed---') this.connentStatue = false // 关闭时,如果客户端还在,需要重连,页面关闭就彻底关闭。 if(heartBeatTimer){ // 关闭时,清除heartBeatTimer定时器 clearInterval(heartBeatTimer) heartBeatTimer = null } if(reconnectingTimer){ clearTimeout(reconnectingTimer) reconnectingTimer = null } this.reconnect() } handleError(e){ console.log('--- Client is error---',e) this.connentStatue = false this.reconnect() } handleMessage(data){ // console.log('--- Client is msg---') const { mode, msg} = this.receiveMsg(data) switch(mode){ case WS_MODE.MESSAGE: console.log('--- MESSAGE ---',msg) break; // 此时接收到消息了,客户端和服务端肯定是连接状态, case WS_MODE.HEART_BEAT: this.connentStatue = true; console.log('--- HEART_BEAT ---',msg) break; default: break; } } // 计时器 startHeartBeat(){ heartBeatTimer = setInterval(()=>{ // 告诉服务端,来了一个HEART_BEAT的消息 this.sendMsg({ mode:WS_MODE.HEART_BEAT, msg:'HEART_BEAT' }) // 模拟关闭,每隔4秒关闭一遍,导致重连reconnect this.waitForResponse() },4000) } // 延迟重连 reconnect(){ // 我们这样子写,由于这个是异步的,我们不能保证能接收到 // reconnectingTimer = setTimeout(()=>{ // Ws.create(this.wsUrl) // },3000) // 所以利用promise return new Promise(resolve=>{ reconnectingTimer = setTimeout(()=>{ resolve(Ws.create(this.wsUrl)) },3000) }) } waitForResponse () { this.connentStatue = false setTimeout(()=>{ // 如果还是连接状态,继续走心跳机制 if(this.connentStatue){ return this.startHeartBeat() } // 要不然刻意关闭,这时候this.connentStatue = false断开连接在执行close, // 肯定会捕获一个错误 try { this.close() } catch (e){ console.log('Client is closed',e) } },2000) } // 将data换成js对象 receiveMsg({ data }){ return JSON.parse(data) } // 发送信息,用字符串 sendMsg(data){ return this.send(JSON.stringify(data)) } static create(url){ return new Ws(url) } } export default Ws;

这个错误就是sendMsg错误,是因为在close的时候,我们就不能再发信息了,因为在close的状态一定要重连,所以在连接状态时才发送消息

this.connentStatue && this.sendMsg({ mode:WS_MODE.HEART_BEAT, msg:'HEART_BEAT' })

此时计时器还是有问题,我们模拟关闭的时候waitForResponse ,不需要重新执行startHeartBeat

waitForResponse () { this.connentStatue = false setTimeout(()=>{ this.close() },2000) }

我们执行的时候,在连接时是可以发送消息的,关闭时就不行了,所以客户端的代码得修改了,因为在重连的时候得接收新的ws,还有重连时这里要重新将实例交给ws,可以写一个发布订阅监听,这里直接将重连函数传进去。

注意:WebSocket是有状态判断的,1为 OPEN,WebSocket的链接已经建立 ;2 为CLOSING 连接正在关闭 ;3 为CLOSED 连接已经关闭或不可用

下面是完整代码

客户端App.vue

Send import Ws from './Ws' // 实例 let ws = null; // 创建 // const init = async ()=>{ // // 实例化一个WebSocket的对象,要用await,比如重连肯定是异步的,需要返回一个ws的对象 // // create的时候相当于new Ws // ws = await Ws.create('ws://localhost:8000') // } function wsConnect(){ /** * 重连时这里要重新将实例交给ws * 1.发布订阅监听 * 2.直接传 */ ws = Ws.create('ws://localhost:8000',wsReConnect) } function wsReConnect(){ // 如果ws不存在,我们就重新创建ws if(!ws){ return wsConnect() } if(ws && ws.reconnectingTimer){ clearTimeout(ws.reconnectingTimer) ws.reconnectingTimer = null; wsConnect() } } const sendMsg = ()=>{ // 用户信息, HEART_BEAT封装在Ws中 ws.sendMsg({ mode:'MESSAGE', msg:'Hello' }) } wsConnect()

客户端Ws.js

const WS_MODE = { MESSAGE:'MESSAGE', HEART_BEAT:'HEART_BEAT' } // 继承WebSocket // 当new Ws的时候,就相当于new一个WebSocket的对象 class Ws extends WebSocket{ // 当new的时候填写的url,相当于在这加一个url,相当于调constructor // url是 ws://localhost:8000 constructor(url,wsReConnect){ // 将url交给super,则是WebSocket的constructor super(url) // 当实例化重连的时候,还得需要url,名字不能起this.url,因为WebSocket实例中有url this.wsUrl = url this.heartBeatTimer = null this.reconnectingTimer = null this.wsReConnect = wsReConnect this.init() } // init 相当于一个开关 init(){ this.bindEvent() } // 绑定函数,如open、close、error、message bindEvent(){ // this:Ws的实例,监听open、close、error、message this.addEventListener('open',this.handleOpen,false) this.addEventListener('close',this.handleClose,false) this.addEventListener('error',this.handleError,false) this.addEventListener('message',this.handleMessage,false) } handleOpen(){ console.log('--- Client is connected---') // 连接时,开启心跳机制,检查是否断开,断开需要重连 // 在startHeartBeat肯定要做间隙interval,则在reconnect中需要做延时进行重连,则需要两个计时器 this.startHeartBeat() } handleClose(){ console.log('--- Client is closed---') // 关闭时,如果客户端还在,需要重连,页面关闭就彻底关闭。 if(this.heartBeatTimer){ // 关闭时,清除this.heartBeatTimer定时器 clearInterval(this.heartBeatTimer) this.heartBeatTimer = null } if(this.reconnectingTimer){ clearTimeout(this.reconnectingTimer) this.reconnectingTimer = null } this.reconnect() } handleError(e){ console.log('--- Client is error---',e) this.reconnect() } handleMessage(data){ // console.log('--- Client is msg---') const { mode, msg} = this.receiveMsg(data) switch(mode){ case WS_MODE.MESSAGE: console.log('--- MESSAGE ---',msg) break; // 此时接收到消息了,客户端和服务端肯定是连接状态, case WS_MODE.HEART_BEAT: console.log('--- HEART_BEAT ---',msg) break; default: break; } } // 计时器 startHeartBeat(){ this.heartBeatTimer = setInterval(()=>{ // 告诉服务端,来了一个HEART_BEAT的消息,this.connentStatue为真时,才发送消息, // 如果关闭时也发送会报错 if(this.readyState === 1){ this.sendMsg({ mode:WS_MODE.HEART_BEAT, msg:'HEART_BEAT' }) }else{ clearInterval(this.heartBeatTimer) this.heartBeatTimer = null } // 模拟关闭,导致重连reconnect this.waitForResponse() },4000) } // 延迟重连 reconnect(){ this.reconnectingTimer = setTimeout(()=>{ this.wsReConnect() },3000) } waitForResponse () { setTimeout(()=>{ this.close() },2000) } // 将data换成js对象 receiveMsg({ data }){ return JSON.parse(data) } // 发送信息,用字符串 sendMsg(data){ this.readyState === 1 && this.send(JSON.stringify(data)) } static create(url,wsReConnect){ return new Ws(url,wsReConnect) } } export default Ws;

服务端app.js

// 导入 const Websocket = require('ws') // 创建一个server,Websocket中有一个Server的属性,注意:是new了一个Websocket的Server,里面给一个端口 const server = new Websocket.Server({ port:8000 }) // 监听 server.on('connection',handleConnection) // 事件处理函数,里面带有wx的实例,也就是WebSocket的实例 function handleConnection(wx){ console.log('---server is connection---') // 监听关闭事件 wx.on('close',handleClose) // 监听错误事件 wx.on('error',handleError) // 监听来消息的时候 wx.on('message',handleMessage) function handleClose(){ // 这个在服务器打印的 console.log('---server is closed---') // 发送一个消息,让客户端接收到已经关闭了 /** 模式:MESSAGE,有可能不是MESSAGE模式,就比如心跳机制,有可能是HEART_BEAT模式, * 但在用户发送消息的时候,mode应该是MESSAGE * 就是有两种形式:一种是心跳机制,客户端向服务端发送消息,另一种是用户在客户端输入消息发送消息给客户端 */ this.send(JSON.stringify({ mode:'MESSAGE', msg:'---server is closed---' })) } function handleError(e){ console.log('---server is Error---',e) } /** * 接收一个data,客户端如何发送消息的呢,发送一个对象 * { * mode:'MESSAGE' or 'HEART_BEAT' * msg:string * } */ function handleMessage(data){ const { mode, msg } = JSON.parse(data) switch(mode){ case 'MESSAGE': // 根据需求处理 console.log('---User message---') this.send(JSON.stringify(JSON.parse(data))) break; case 'HEART_BEAT': console.log('---HeartBeat message---') this.send(JSON.stringify(JSON.parse(data))) break; default: break; } } }



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3