/** * `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(); };