WebSocket基本原理和心跳机制

WebSocket 基本概念

WebSocket 是HTML5下一种新的计算机网络应用层的协议,是基于HTTP协议的,实现了客户端与服务端的全双工通信,客户端可以主动向服务端发起请求,服务端也可以主动向客户端推送数据。并且只需要第一次由客户端发起请求连接,连接成功后,客户端与服务端保持长久的连接,后续数据都以帧序列的形式传输。

  • 单工通信,只支持数据传输在一个方向传输
  • 半双工通信,允许数据在两个方向上传输,但同一时间只允许一个方向上的数据通信
  • 全双工通信,允许数据在两个方向上同时传输

建立 WebSocket 连接

客户端创建 WebSocket 对象(new WebSocket()),发起请求(ws://www.example.com/),与服务端进行 tcp 三次握手建立连接。
请求报文:

1
2
3
4
5
6
7
GET /webfin/websocket/ HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: xqBt3ImNzJbYqRINxEFlkg==
Origin: http://localhost:8080
Sec-WebSocket-Version: 13

Upgrade: websocketConnection: Upgrade 两个字段告知服务端,此次发起的是 Websocket 协议的请求。Sec-WebSocket-Key 则是客户端随机生成的,用于于服务端进行通信认证。
响应报文:

1
2
3
4
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: K7DJLdLooIwIG/MOpvWFB3y3FE8=

Sec-WebSocket-Accept 的值是服务端通过与客户端相同的密钥计算得到的
与服务端建立连接后,则没有 HTTP 啥事了,后续将直接由 WebSocket 协议负责客户端与服务端之间的通信。

WebSocket 基本属性和方法

构造函数 WebSocket(),客户端通过 new WebSocket(),传入 url 创建 WebSocket 实例。通过 onopen() 方法用于指定连接成功后的回调函数,onclose() 方法用于指定连接关闭后的回调函数,onmessage() 方法用于指定收到服务端数据后的回调函数,onerror() 方法用于指定连接失败后的回调函数

客户端实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let ws = new WebSocket('ws://localhost:8080/')
ws.onopen = function () {
ws.send('Hello world')
}
ws.onclose = function (event) {
console.log(event.code)
// or reconnect...
}
ws.onmessage = function (event) {
let data = event.data
// handle data
}
ws.onerror = function (event) {
// handle error event or reconnect
}

服务端实现

1
2
3
4
5
6
7
8
let WebSocketServer = require('ws').Server,
wss = new WebSocketServer({ port: 8080 });
wss.on('connection', function (ws) {
console.log('client connected');
ws.on('message', function (message) {
console.log(message);
});
});

WebSocket 重连机制

基本思路就是,在 WebSocket 连接关闭或者连接异常时,重新发起连接就可以了

1
2
3
4
5
6
7
ws.onclose = function (event) {
reconnect()
}
ws.onerror = function (event) {
reconnect()
}

但是在使用 WebSocket 过程中,可能会出现网络断开的情况,比如信号不好,或者网络临时性关闭,这时候 WebSocket 的连接已经断开,而浏览器不会执行 WebSocket 的 onclose 方法,我们无法知道是否断开连接,也就无法进行重连操作。
这时候需要另一种重连机制—— WebSocket 心跳重连机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let timeout = 10000 // 与服务端断开后需要重连的时间
let timer = null
let heartConnect = {
reset () {
clearTimeout(timer)
this.start()
},
start () {
timer = setTimeout(() => {
ws.send('Heart beat')
}, timeout)
}
}
let ws = new WebSocket('ws://localhost:8080/')
ws.onopen = function () {
heartConnect.start()
};
ws.onmessage = function (event) {
heartConnect.reset()
}

心跳的基本思路是:通过定时器,当成功连接上时,开始计时,如果在定时器间隔内,收到服务端推送的消息,则重置定时器。如果超过60s还没收到服务端消息,则执行心跳检测,WebSocket 会向服务端发送消息,发送超时后,客户端会自动触发 onclose 方法,WebSocket 重连也就执行了。

WebSocket 的优劣势

优:只要建立一次连接,就可以连续不断的得到服务器推送的消息,节省带宽和服务器端的压力

劣:不兼容低版本浏览器

与Ajax 轮询、长轮询(long poll)比较

ajax轮询的原理非常简单,让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息。

long poll 其实原理跟 ajax轮询 差不多,都是采用轮询的方式,不过采取的是阻塞模型,客户端发起连接后,如果没消息,就一直不返回 Response 给客户端。直到有消息才返回,返回完之后,客户端再次建立连接。

Ajax轮询需要服务器有很快的处理速度与快速响应。long poll 需要很高的并发,体现在同时容纳请求的能力

坚持原创技术分享,您的支持将鼓励我继续创作!