WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。传统的http协议,通信只能由客户端发起。websocket实现了客户端和服务端的双向平等对话,websocket最大的特点:服务器可以主动向用户推送信息,客户端也可以主动向服务端发送信息。

一、websocket特点:

1、建立在 TCP 协议之上,服务器端的实现比较容易。
2、与 HTTP 协议有着良好的兼容性,默认端口也是80和443。并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
3、数据格式比较轻量,性能开销小,通信高效。
4、可以发送文本,也可以发送二进制数据。
5、没有同源限制,客户端可以与任意服务器通信。
6、全双工(通信允许数据在两个方向上同时传输,它在能力上相当于两个单工通信方式的结合,例如指 A→B 的同时 B→A ,是瞬时同步的)
7、协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

初始化websocket
1、创建websocket实例,参数为url
2、连接 websocket.onopen
3、server响应数据触发 websocket.onmessage
4、关闭websocket,websocket.onclose

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 构造函数 参数为url
const wsurl = "ws://localhost:8080";
var ws = new Websocket(wsurl);

// 连接状态readState 1 准备好发送和接受数据了
ws.onopen = function(){
ws.send('hello server! websocket is open now!')
}

// 通过客户端的事件 发送信息给服务端
ws.send('hello websocket');

// 发生错误
ws.onerror = function(event){
console.log("websocket error observed",event)
}

// readState CLOSED 关闭websocket
ws.onclose = function(event){
let status_code = event.status;
let msg = event.msg;
console.log("websocket is closed now")
}

// 响应数据的接收
ws.onmessage = function(event){
let data = event.data;
}
  • 路由改变,需要断开websocket连接,节省服务器开支。

二、心跳机制

websocket在连接关闭的情况下触发onclose事件,连接异常触发onerror事件。网络状态不好的情况,onclose事件的触发灵敏度不高,可能会造成断网很久触发onclose事件,客户端又出现重新连接,客户端实时界面不友好。

为了解决上面的情况,使用心跳重连机制,客户端在websocket连接成功后,执行心跳函数,首先向服务器发送’ping‘信息,服务器收到信息会返回’pong’信息。一定时间内,客户端收到服务器返回的信息,则表示连接正常,重置心跳函数;客户端在一定时间内没有收到心跳函数,表明没有连接成功,客户端关闭websocket,再执行重连操作。

解决方式:

点我展示代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import { mapActions, mapState } from 'vuex';
export default {
name: 'Websocket',
data() {
return {
// 是否正在重连
lockReconnect: false,
socket: null,
reconnectTimeout: null,
timeout: 10 * 1000,
timer: null,
serverTimer: null
};
},
computed: {
...mapState(['userInfo']),
wsuri() {
return `${process.env.VUE_APP_WEBSOCKET_URI}${this.userInfo.tenantId};${this.userInfo.userId}`;
}
},
async mounted() {
await this.getUserInfo();
this.initWebSocket();
},
destroyed() {
this.socket.close();
},
methods: {
...mapActions(['getUserInfo']),
start(ws) {
this.reset();
this.timer = setTimeout(() => {
// console.log('发送心跳,后端收到后,返回一个心跳消息')
// onmessage拿到返回的心跳就说明连接正常
ws.send('ping');
this.serverTimer = setTimeout(() => {
// 如果超过一定时间还没响应(响应后触发重置),说明后端断开了
ws.close();
}, this.timeout);
}, this.timeout);
},
reset() {
this.serverTimer && clearTimeout(this.serverTimer);
this.timer && clearTimeout(this.timer);
},
reconnect() {
console.log('尝试重连');
if (this.lockReconnect) {
return;
}
this.lockReconnect = true;
this.reconnectTimeout && clearTimeout(this.reconnectTimeout);
this.reconnectTimeout = setTimeout(() => {
this.initWebSocket();
this.lockReconnect = false;
}, 4 * 1000);
},
// 初始化websocket
initWebSocket() {
try {
if ('WebSocket' in window) {
this.socket = new WebSocket(this.wsuri);
} 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);
this.start(this.socket);
this.websocketSend();
},
websocketOnError(e) {
console.log('WebSocket连接发生错误', e);
this.reconnect();
},
websocketOnMessage(e) {
if (e.data === 'pong') {
// 消息获取成功,重置心跳
this.start(this.socket);
}
},
websocketClose(e) {
console.log('connection closed (' + e.code + ')');
this.reconnect();
},
websocketSend() {
this.socket.send('ping');
}
}
};