我正在参加「码上掘金挑战赛」详情请看:码上掘金挑战赛来了!
前言
websocket是什么呢?官方的解释是浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
我的理解是搭建一个前后端的桥梁,可以实时发送消息。例如我登录了网站以后,有什么新的新闻动态立马给我弹窗提示。不能总是前端循环调接口看看后端有没有新闻吧。
最近我也是做到一个WebSocket相关的需求,也记录一下。
一、创建ws对象
var ws = new WebSocket(url, [protocol] );
- url:访问后端的地址,ws或者wss开头,如:
ws://localhost:3000/websocket。(WSS(Web Socket Secure)是WebSocket的加密版本) - protocol:需要放到headers中传给后端的参数,如需要验证身份的token。可传多个,逗号隔开。
二、几种状态
| readyState属性值 | 对应的状态 |
|---|---|
| 0 | 正在建立连接连接,还没有完成 |
| 1 | 连接成功建立,可以进行通信 |
| 2 | 连接正在进行关闭握手,即将关闭 |
| 3 | 连接已经关闭或者根本没有建立 |
三、使用WebSocket
普通的js简单调用
var ws = new WebSocket("ws://localhost:3000/websocket");//连接服务器
ws.onopen = function(e){alert("已经与服务器建立了连接。当前连接状态:"+readyState);};
ws.onmessage = function(res){alert("接收到服务器发送的数据:"+res.data);};
ws.onclose = function(e){alert("已经与服务器断开连接");};
ws.onerror = function(e){alert("WebSocket连接失败!");};
普通的vue简单调用
- 只演示简单例子,如果需要多次调用可以封装起来使用
<template>
<div>
websocket示例
</div>
</template>
<script>
export default {
name: 'Websocket',
data() {
return {
url: ws://localhost:3000/websocket', // ws wss
socket: null
}
},
mounted() {
this.initWebSocket()
},
methods: {
reconnect() {
console.log('尝试重连')
setTimeout(() =>
this.initWebSocket()
}, 60 * 1000)// 加个延时
},
initWebSocket() {
try {
if ('WebSocket' in window) {
this.socket = new WebSocket(this.url)
} else {
console.log('您的浏览器不支持websocket')
}
this.socket.onopen = this.websocketonopen
this.socket.onerror = this.websocketonerror
this.socket.onmessage = this.websocketonmessage
this.socket.onclose = this.websocketclose
} catch (e) {
this.reconnect()
}
},
websocketonopen() {
console.log('WebSocket连接成功', this.socket.readyState)
},
websocketonerror(e) {
console.log('WebSocket连接发生错误', e)
this.reconnect()
},
websocketonmessage(res) {
let data = JSON.parse(res.data)
console.log('响应数据:', data)
},
websocketclose(e) {
console.log('连接中断了' + e )
}
},
destroyed() {
this.socket.close()
}
}
</script>
配置代理
- 在vue.config.js里配置websocket的代理
module.exports = {
devServer: {
proxy: {
'/ws': { // websocket代理
target: '代理目标的基础路径',
changeOrigin: true,
ws:true // 开启websocket代理
}
// ...其他代理
}
}
}
Nginx配置
此处不是我配置的,我就不写啦
websocket心跳机制
(1)为什么增加websocket心跳?
-
在使用websocket的过程中,有时候会遇到网络断开的情况,但是在网络断开的时候服务器端并没有触发onclose的事件。服务器会继续向客户端发送多余的链接,并且这些数据还会丢失。
-
这个问题被发现是因为有次执行,等待时间特别长,我们的使用nginx代理,nginx配置了访问超时时间。在测试环境就有问题了,在我本地没有问题。
-
所以需要加上websocket的心跳,还有心跳,说明还活着,没有心跳说明已经关闭了。
-
因为我这个业务,有两个页面需要用到websocket,所以把它封装到了一个js文件
(2)websocket心跳原理
- 创建一个定时器,每隔一段时间就send一次信息,这样Nginx就不会自己断websocket了(发送心跳)
- 如果收到后台返的消息,就重置定时器,说明心跳没有断(重置心跳)
- 后端无需返回相应的心跳也没有问题。
- 完整代码如下:
class BaseWebSocket {
constructor(options) {
this.ws = null;
this.url = options.url;
this.status = null;
this.isHeart = options.isHeart; // 是否开启心跳
this._timeout = 30000; // 每隔时间发送心跳
this.msgCallback = options.msgCallback; // 收到消息的回调
this.pingInterval = null; // 定时器
this.lockReconnect = false;
this.isReconnec = options.isReconnec; // 是否重连
this.connect();
}
connect() {
if (this.lockReconnect) {
return;
}
this.lockReconnect = true;
this.createWebSocket();
this.lockReconnect = false;
}
createWebSocket() {
if ('WebSocket' in window) {
this.ws = new WebSocket(this.url);
} else if ('MozWebSocket' in window) {
this.ws = new MozWebSocket(this.url);
} else {
console.log('您的浏览器不支持websocket协议,建议使用新版谷歌、火狐等浏览器,请勿使用IE10以下浏览器')
}
this.initEvent();
}
initEvent() {
this.ws.onopen = (e) => {
this.status = 'open';
if(this.isHeart) {
this._heartCheck()
}
}
this.ws.onmessage = (e) => {
if(typeof this.msgCallback === 'function'){
return this.msgCallback(e.data)
}else{
console.log('no function')
}
}
this.ws.onclose = (e) => {
this._close(e)
}
this.onerror = (e) => {
this._close(e)
}
}
_resetHeart() {
clearInterval(this.pingInterval)
return this
}
_heartCheck() {
this.pingInterval = setInterval(() => {
if(this.ws.readyState === 1) {
this.ws.send(JSON.stringify({type: 'ping'}))
}
}, this._timeout)
}
_close(e) {
this._resetHeart()
if(this.status !== 'close') {
if(this.isReconnec){
this.connect()
}
}else{
console.log(e)
}
}
close() {
this.status = 'close'
this._resetHeart()
return this.ws.close();
}
send(data) {
return this.ws.send(JSON.stringify(data))
}
}
- 调用
import BaseWebSocket from '...'
const ws = new webSocket({
url: 'ws://...',
isHeart: true,
isReconnec: true,
msgCallback:this.msgCallback
});
msgCallback(data){
// 处理收到消息的业务
}