前言:在使用settimeout发送心跳消息有问题,有可能是封装的方法没写好或者其他原因吧,不懂呀,最后解决是解决了(最终代码在最下面),就是还是有很多不懂的地方需要摸索探查
一,方法一(settimeout):会出现断连问题
刚开始用的是settimeout定时发送心跳消息,但中途会出现断连
参考的是如下方法:
//心跳检测
var heartCheck = {
timeout: 5000, //1分钟发一次心跳
timeoutObj: null,
serverTimeoutObj: null,
reset: function () {
clearTimeout(this.timeoutObj);
clearTimeout(this.serverTimeoutObj);
return this;
},
start: function () {
var self = this;
this.timeoutObj = setTimeout(function () {
//这里发送一个心跳,后端收到后,返回一个心跳消息,
//onmessage拿到返回的心跳就说明连接正常
send("ping");
self.serverTimeoutObj = setTimeout(function () { //如果超过一定时间还没重置,说明后端主动断开了
websocket.close(); //如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次
}, self.timeout)
}, this.timeout)
}
}
// 使用,在连接成功建立的回调方法内使用示例(其他的部分就不写了):
websocket.onopen = function (event) {
heartCheck.reset().start();
}
不知道是因为什么引起的问题,反正心跳消息发着发着某个时候突然就不发了,定时器似乎就像是停止工作了
二、方法二:仍然出现断连
之后又用了一个方法requestAnimationFrame
参考文章是这个:(链接中有个使用方法括号没写会有影响,自己可以改一下试试)
经过尝试后发现,仍然是某个时刻突然平白无故断开,
我之后再找时间考察一下原因吧......
三、直接使用setInterval处理心跳消息
在这两个方式都还是会存在不稳定或者断开的情况,我开始觉得找寻其他方法
试了下直接用setInterval方法,
连接上直接发送ping消息,之后只要每次后端发动pong消息这边就清除定时器后再次发送ping消息,依靠每次收到的后端发过来的pong消息稳定维持发送。
后面表现也很稳定,连接一整天都不会断,代码如下:
$(function () {
// 监听网络链接(首次链接不会触发)
window.addEventListener("online", function () {
reconnect()
})
// 监听网络断开
window.addEventListener("offline", function () {
clearInterval(heartBeat);
websocket.close();
})
window.onunload = function (event) {
// 清除表单输入
// 清除cookie
// 撤销订阅的事件和定时器
// ......
};
var uniqueId = "***"
var websocket = null;
var lockReconnect = false; //避免ws重复连接
var time = Date.parse(new Date()) / 1000
var sign = "***......"
var url = "ws://1.234.56.78:9999?sign=" + sign + "&zuoxi=" + uniqueId + "&time=" + time
var heartBeat
//判断当前浏览器是否支持WebSocket, 主要此处要更换为自己的地址
if ('WebSocket' in window) {
connectSocket();
} else {
alert('Not support websocket')
}
//连接webSocket
function connectSocket() {
try {
time = Date.parse(new Date()) / 1000
sign = "***......"
url = "ws://1.234.56.78:9999?sign=" + sign + "&zuoxi=" + uniqueId + "&time=" + time
websocket = new WebSocket(url);
initEvent();
} catch (e) {
reconnect();
console.log(e);
}
}
function initEvent() {
//连接发生错误的回调方法
websocket.onerror = function (e) {
reconnect();
// console.log("ws连接错误!");
};
//连接成功建立的回调方法
websocket.onopen = function (event) {
send("PING")
heartBeat = setInterval(function () {
send("PING")
}, 10000);
// ajax查询用户状态
// ......
// console.log("ws连接成功!");
}
//接收到消息的回调方法
websocket.onmessage = function (event) {
if (event.data === 'ping消息过期,客户端将被关闭') {
clearInterval(heartBeat);
websocket.close();
reconnect()
return
} else if (event.data == "PONE") {
clearInterval(heartBeat);
heartBeat = setInterval(function () {
send("PING")
}, 10000);
return
} else if (event.data == "通信ID无效") {
// 用户已经下线,不可发送消息!
return
} else {
// 用户消息
var fromUserMsg = JSON.parse(event.data)
if (fromUserMsg.type == "onLine") {
// 用户上线
// ......
} else if (fromUserMsg.type == "offLine") {
// 用户下线
// ......
} else {
// 用户发了消息
// ......
}
}
}
//连接关闭的回调方法
websocket.onclose = function (e) {
clearInterval(heartBeat);
console.log("ws连接关闭!");
}
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {
clearInterval(heartBeat);
closeWebSocket();
}
//关闭连接
function closeWebSocket() {
websocket.close();
}
//发送消息
function send(message) {
if (message == "PING") {
websocket.send(message);
return
}
let connectId = 123
var data = {
class: "消息",
type: "onMessage",
text: {
data: message,
connectId: connectId,
}
}
websocket.send(JSON.stringify(data));
}
//重连
function reconnect() {
if (lockReconnect) return;
lockReconnect = true;
setTimeout(function () { //没连接上会一直重连,设置延迟避免请求过多
connectSocket();
lockReconnect = false;
}, 100);
}
// 为发送按钮绑定鼠标点击事件
$('body').on('click', "#btnSend", function () {
var sendTime = new Date().getTime()
var text = $('#ipt').val().trim()
if (text.length <= 0) {
return $('#ipt').val('')
}
let sendText = {
text: text,
msgNewTime: sendTime
}
JSON.stringify(sendText)
send(sendText)
// 记录聊天内容
// 如果用户输入了聊天内容,则将聊天内容追加到页面上显示
// 重置滚动条的位置
})
})