预备代码
为描述方便,先将简单的 Websocket 连接函数 ws_connect()
贴出来,ws
为 Websocket 对象:
var ws;
/**
* 连接 websocket
* @param func onopen要执行的函数,可以为空
*/
function ws_connect(func) {
ws = new WebSocket("ws://" + ws_ip);
// 服务端主动推送消息时会触发这里的 onmessage
ws.onmessage = function (e) {
console.log('ws_onmessage ');
};
ws.onopen = function (e) {
console.log('ws_onopen');
// 开启心跳
ws_heart();
if (typeof func == 'function') {
func();
}
};
ws.onerror = function (e) {
console.error("ws_onerror:", e);
};
ws.onclose = function (e) {
console.log('ws_onclose:code:' + e.code + ';reason:' + e.reason + ';wasClean:' + e.wasClean);
};
}
$(function () {
var func = function () {
var data = {type: 'login'};
ws.send(JSON.stringify(data));
};
// 页面加载时第一次连接,也可以传空
ws_connect(func);
});
解决方案
断线重连
重新连接的时候 Websocket 的属性 readyState 有着至关重要的作用,先了解一下这个属性的含义(以下其中之一):
0 (CONNECTING)
正在链接中1 (OPEN)
已经链接并且可以通讯2 (CLOSING)
连接正在关闭3 (CLOSED)
连接已关闭或者没有链接成功
除了第一次连接,每一次连接时都必须考虑当前的连接状态,比如我要执行 ws.send(data);
,在四种状态下的执行时机是不同的,如下:
0 (CONNECTING)
正在链接中 - 等连接成功后,在ws.onopen
里执行1 (OPEN)
已经链接并且可以通讯 - 直接执行就是了2 (CLOSING)
连接正在关闭 - 等关闭完成后,在ws.onclose
里重新连接,在重连成功的ws.onopen
里执行3 (CLOSED)
连接已关闭或者没有链接成功 - 重新连接,在重连成功的ws.onopen
里执行
这样每次发送数据都能保证连接成功(除非网络断了或服务器挂了),写一个函数 ws_execute()
封装这些操作,这个函数如下:
/**
* 根据连接状态单线程连接 websocket
* @param func onopen要执行的函数,可以为空
*/
function ws_execute(func) {
console.log('ws_execute:readyState:' + ws.readyState);
if (ws.readyState == 0) {
// 正在链接中
var _old$open = ws.onopen;
ws.onopen = function (e) {
// 原本 onopen 里的代码先执行完毕
_old$open.apply(this, arguments);
if (typeof func == 'function') {
func();
}
};
} else if (ws.readyState == 1) {
// 已经链接并且可以通讯
if (typeof func == 'function') {
func();
}
} else if (ws.readyState == 2) {
// 连接正在关闭
var _old$close = ws.onclose;
ws.onclose = function (e) {
// 原本 onclose 里的代码先执行完毕
_old$close.apply(this, arguments);
ws_connect(func);
};
} else if (ws.readyState == 3) {
// 连接已关闭或者没有链接成功
ws_connect(func);
}
}
业务逻辑里发送数据是这样的(代码片断):
// 发送数据时,将代码构造成函数作为参数,等 onopen 时执行
var func = function () {
var data = {type: 'audio'};
ws.send(JSON.stringify(data));
};
ws_execute(func);
心跳
有了上面的 ws_execute()
函数,心跳就简单了,比如每1分钟向服务器发送一次数据:
var ws_heart_i = null;
/**
* websocket 每1分钟发一次心跳
*/
function ws_heart() {
if (ws_heart_i) clearInterval(ws_heart_i);
ws_heart_i = setInterval(function () {
console.log('ws_heart');
var func = function () {
var data = {type: 'ping'};
ws.send(JSON.stringify(data));
};
ws_execute(func);
}, 60000);
}
把 ws_heart()
函数放在 ws.onopen
里就可以了。