导读:前后端实时通信常用的一种方式。
websocket 是一种采用socket 通信的连接,而不是http协议所以不受浏览器SOP的限制,是一种支持跨域访问的协议,客户端可以与任意服务器通信。
1、简单入门
1.1 初始化websocket
initWebsockt() {
if (typeof WebSocket === 'undefined') {
console.log('您的浏览器不支持WebSocket')
return
} else {
// 建立连接
this.websocket = new WebSocket(this.url)
// 连接成功
this.websocket.onopen = this.onOpen
// 客户端接收服务端返回的数据
this.websocket.onmessage = this.onMessage
// 发生错误时
this.websocket.onerror = this.onError
// 关闭连接时
this.websocket.onclose = this.onClose
}
},
1.2 定义事件处理函数
// 连接成功事件
onOpen() { },
// 接收数据
onMessage(data) {
console.log(JSON.parse(data))
// todo 处理数据
},
// 连接出错事件
onError() {
this.reconnect()
},
// 连接关闭事件
onClose() {
this.reconnect()
},
// 重连
reconnect() {
setTimeout(() => {
// 建立新连接
this.initWebsockt()
}, 5000)
},
2、进阶 _ 断开重连(心跳检测机制)
为了实现客户端与服务器之间实时通信,就需要两端一直保持TCP长连接不能断开;在使用过程中发现隔一段时间不操作后会自动断开(使用Nginx代理websocket的场景下,Nginx有超时时间,当检测到一段时间没有数据传输后会自动断开)
然而在有些场景下确实存在长时间没有数据往来,但是需要保持连接确保即时通信的情况。解决方法如下:
- Nginx设置超时时间长一些,(服务器端设置,此方法治标不治本)
- 心跳机制
客户端 -> 服务端:ping
服务端 -> 客户端:pong
2.1 心跳机制
data:部分数据初始化
// heartbeat 心跳
heartbeat: {
interval: 30 * 1000, // 心跳间隔时间
timeout: 10 * 1000, // 响应超时时间
clientPingTimer: null, // 延时发送心跳的定时器
servePongTimer: null, // 接收心跳响应的定时器
},
核心代码:startHeartbeat
startHeartbeat() {
const webSocket = this.webSocket
const heartbeat = this.heartbeat
// 重置心跳定时器
this.clearTimeoutObj(heartbeat)
// 等待发送心跳包
heartbeat.clientPingTimer = setTimeout(() => {
// 如果连接正常
if (webSocket.readyState === 1) {
// 发送ping,等待响应 pong
webSocket.send('ping')
// 心跳发送后,如果服务器超时未响应则断开,如果响应了会被重置心跳定时器
heartbeat.servePongTimer = setTimeout(() => {
webSocket.close()
}, heartbeat.timeout)
} else {
// 否则重连
this.reconnect()
}
}, heartbeat.interval)
},
// 清空定时器
clearTimeoutObj: function (heartbeat) {
heartbeat.clientPingTimer && clearTimeout(heartbeat.clientPingTimer)
heartbeat.servePongTimer && clearTimeout(heartbeat.servePongTimer)
},
核心代码:调用发送心跳包
连接成功事件
onOpen() {
// 开启心跳
this.startHeartbeat()
// 重置 重连次数0
this.reconnectTimes = 0
},
接收数据事件
onMessage(res) {
// 心跳
this.startHeartbeat()
const data = res.data
// 心跳响应数据pong不处理
if (data.indexOf('pong') !== -1) {
return
}
// todo 处理数据,展现数据
},
2.2 持续优化-设置最大连接数
maxReconnect: 10, // 最大重连数
reconnectTimes: 0, // 重连次数
2.3 持续优化-重连锁
isLock: false, // 加锁,避免套娃(重连过程中再重连)
2.4 持续优化-重连
reconnect() {
// +++建立连接中,锁了
if (this.isLock) {
return
}
// +++超出最大连接次数
if (this.reconnectTimes > this.maxReconnect) {
return
}
this.isLock = true // +++
setTimeout(() => {
// +++重连次数 +1
this.reconnectTimes++
// 建立新连接
this.initWebsockt()
this.isLock = false // +++
}, 5000)
},
3、 思考
为什么ws有监听断开onclose,在断开里面有重连reconnect;还心跳检测呢? 有一些情况如网络断开不会被onclose监听到,有心跳检测会使连接一直保活,从而避免websocket的断开造成数据丢失。