151 lines
4.3 KiB
JavaScript
151 lines
4.3 KiB
JavaScript
/**
|
||
* `GdooWebSocket` constructor.
|
||
*
|
||
* @param {Object} opts
|
||
* {
|
||
* url websocket链接地址
|
||
* pingTimeout 未收到消息多少秒之后发送ping请求,默认15000毫秒
|
||
pongTimeout 发送ping之后,未收到消息超时时间,默认10000毫秒
|
||
reconnectTimeout 尝试重连的间隔时间,默认2000毫秒
|
||
pingMsg ping 消息值,默认ping
|
||
repeatLimit 重连尝试次数。默认不限制
|
||
* }
|
||
* @api public
|
||
*/
|
||
|
||
var GdooWebSocket = function({
|
||
url,
|
||
pingTimeout = 15000,
|
||
pongTimeout = 10000,
|
||
reconnectTimeout = 2000,
|
||
pingMsg = 'ping',
|
||
repeatLimit = null
|
||
}){
|
||
this.opts = {
|
||
url: url,
|
||
pingTimeout: pingTimeout,
|
||
pongTimeout: pongTimeout,
|
||
reconnectTimeout: reconnectTimeout,
|
||
pingMsg: pingMsg,
|
||
repeatLimit: repeatLimit
|
||
};
|
||
|
||
this.ws = null; //websocket实例
|
||
this.repeat = 0;
|
||
|
||
// override hook function
|
||
this.onclose = () => {};
|
||
this.onerror = () => {};
|
||
this.onopen = () => {};
|
||
this.onmessage = () => {};
|
||
this.onreconnect = () => {};
|
||
this.onsend = () => {};
|
||
this.onreconnectCountDown = () => {};
|
||
|
||
this.createWebSocket();
|
||
}
|
||
|
||
GdooWebSocket.prototype.createWebSocket = function() {
|
||
try {
|
||
this.ws = new WebSocket(this.opts.url);
|
||
this.initEventHandle();
|
||
} catch (e) {
|
||
this.reconnect();
|
||
throw e;
|
||
}
|
||
};
|
||
|
||
GdooWebSocket.prototype.initEventHandle = function() {
|
||
this.ws.onclose = (e) => {
|
||
this.onclose(e);
|
||
this.reconnect();
|
||
};
|
||
this.ws.onerror = (e) => {
|
||
this.onerror(e);
|
||
this.reconnect();
|
||
};
|
||
this.ws.onopen = () => {
|
||
this.repeat = 0;
|
||
this.onopen();
|
||
//心跳检测重置
|
||
this.heartCheck();
|
||
};
|
||
this.ws.onmessage = (event) => {
|
||
this.onmessage(event);
|
||
//如果获取到消息,心跳检测重置
|
||
//拿到任何消息都说明当前连接是正常的
|
||
this.heartCheck();
|
||
};
|
||
};
|
||
|
||
GdooWebSocket.prototype.reconnect = function() {
|
||
if (this.opts.repeatLimit > 0 && this.opts.repeatLimit <= this.repeat) return; //limit repeat the number
|
||
if (this.lockReconnect || this.forbidReconnect) return;
|
||
this.lockReconnect = true;
|
||
this.repeat++; //必须在lockReconnect之后,避免进行无效计数
|
||
|
||
this.onreconnect();
|
||
|
||
// 倒计时重连
|
||
this.countDown = options.reconnectTimeout / 1000;
|
||
this.reconnectCountDown();
|
||
|
||
// 没连接上会一直重连,设置延迟避免请求过多
|
||
setTimeout(() => {
|
||
this.createWebSocket();
|
||
this.lockReconnect = false;
|
||
}, this.opts.reconnectTimeout);
|
||
};
|
||
|
||
GdooWebSocket.prototype.send = function(msg) {
|
||
this.ws.send(msg);
|
||
this.onsend(msg);
|
||
};
|
||
|
||
//心跳检测
|
||
GdooWebSocket.prototype.heartCheck = function() {
|
||
this.heartReset();
|
||
this.heartStart();
|
||
};
|
||
|
||
GdooWebSocket.prototype.heartStart = function() {
|
||
// 不再重连就不再执行心跳
|
||
if (this.forbidReconnect) return;
|
||
|
||
this.pingTimeoutId = setTimeout(() => {
|
||
// 这里发送一个心跳,后端收到后,返回一个心跳消息,
|
||
// onmessage拿到返回的心跳就说明连接正常
|
||
this.ws.send(this.opts.pingMsg);
|
||
this.onsend(this.opts.pingMsg);
|
||
|
||
// 如果超过一定时间还没重置,说明后端主动断开了
|
||
this.pongTimeoutId = setTimeout(() => {
|
||
// 如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次
|
||
this.ws.close();
|
||
}, this.opts.pongTimeout);
|
||
|
||
}, this.opts.pingTimeout);
|
||
};
|
||
|
||
GdooWebSocket.prototype.reconnectCountDown = function() {
|
||
this.onreconnectCountDown(this.countDown);
|
||
if (this.countDown > 0) {
|
||
this.countDownTimeoutId = setTimeout(() => {
|
||
this.reconnectCountDown();
|
||
}, 1000);
|
||
}
|
||
this.countDown--;
|
||
}
|
||
|
||
GdooWebSocket.prototype.heartReset = function() {
|
||
clearTimeout(this.pingTimeoutId);
|
||
clearTimeout(this.pongTimeoutId);
|
||
clearTimeout(this.countDownTimeoutId);
|
||
};
|
||
|
||
GdooWebSocket.prototype.close = function() {
|
||
// 如果手动关闭连接,不再重连
|
||
this.forbidReconnect = true;
|
||
this.heartReset();
|
||
this.ws.close();
|
||
}; |