interface Class2Type {
[key: string]: string;
}
export function _typeof(value: any) {
const class2type: Class2Type = {};
"Boolean Number String Function Array Date RegExp Object Error".split(" ").forEach((key) => {
class2type["[object " + key + "]"] = key.toLowerCase();
});
if (value === null) return String(value);
return typeof value === "object" || typeof value === "function" ? class2type[class2type.toString.call(value)] || "object" : typeof value;
}
/**
* websocket 类
* @class
* @param {object|string} options-传递参数
* @param {string} options.url-websocket 连接地址
* @param {string|object[]} [options.protocol]-子协议
* @param {reconnect} [options.reconnect]-断开后是否自动连接
* @param {number} [reconnectIntervalInMilliSeconds]-连接时间
**/
interface Options {
url: string;
reconnect?: boolean;
reconnectIntervalInMilliSeconds?: number;
}
interface Watcher {
open: any[];
message: any[];
close: any[];
error: any[];
}
type WebSocketPayload = string | ArrayBuffer | Blob
interface WebSocketLike {
close(): any;
send(data: WebSocketPayload): any;
onopen: ((event: any) => any) | null;
onclose: ((event: any) => any) | null;
onmessage: ((event: any) => any) | null;
onerror: ((event: any) => any) | null;
readyState: number;
}
const defaultOptions = {
url: "",
reconnect: true,
reconnectIntervalInMilliSeconds: 0,
};
class WebSocketClass {
private ws: WebSocketLike | null;
private reconnectTimeOut: null | number;
private readonly url: string;
private readonly reconnect: boolean;
private readonly reconnectIntervalInMilliSeconds: number;
private isDestroy: boolean;
private attempts: number;
private serverHeartTimeout: null | number;
private heartTimeout: null | number;
private heartTime: number;
private heart: boolean;
private watcher: Watcher;
private isReconnected: boolean;
constructor(options: string | Options) {
let _options = {};
if (_typeof(options) === 'string') {
_options = { url: options };
} else if (_typeof(options) === 'object' && options !== null) {
_options = options;
}
const option = Object.assign({}, defaultOptions, _options);
this.ws = new WebSocket(option.url);
this.url = option.url;
this.reconnect = option.reconnect;
this.reconnectIntervalInMilliSeconds = option.reconnectIntervalInMilliSeconds;
this.attempts = 1;
this.isDestroy = false;
this.reconnectTimeOut = null;
this.heart = false;
this.heartTime = 40000;
this.heartTimeout = null;
this.serverHeartTimeout = null;
this.isReconnected = false; // 是否在重连中
this.watcher = {
open: [],
message: [],
error: [],
close: [],
};
this._init();
}
// Websocket 开启连接时执行
onOpen(callback: () => void) {
this.watcher['open'].push(callback);
}
// websocket 在通信中执行函数
onMessage(callback: () => void) {
this.watcher['message'].push(callback);
}
// websocket 断开连接执行函数
onClose(callback: () => void) {
this.watcher['close'].push(callback);
}
// 销毁当前websocket
destroy() {
this.isDestroy = true;
if (this.ws) this.ws.close();
this.ws = null;
// 清除延时
clearTimeout(this.heartTimeout!);
clearTimeout(this.serverHeartTimeout!);
clearTimeout(this.reconnectTimeOut!);
this.heartTimeout = null;
this.serverHeartTimeout = null;
this.reconnectTimeOut = null;
}
// 重连
doReconnect() {
if (this.reconnect) {
// 存在ws 则先关闭
if (this.ws) {
this.ws.close();
this.ws = null;
}
// 主动destroy,不进行重连
if (this.isDestroy) return;
// 重连判断 在重连中则返回
if (this.isReconnected) return;
this.isReconnected = true;
const time = this._generateInterval(this.attempts);
this.reconnectTimeOut = setTimeout(() => {
this.attempts += 1;
this.isReconnected = false;
this.ws = new WebSocket(this.url);
this._init();
}, time);
}
}
// 初始化
_init() {
this.ws!.onopen = () => {
this.watcher['open'].forEach(fn => fn());
this.sendHeart();
};
this.ws!.onmessage = (event: any) => {
// 过滤心跳数据
const data = JSON.parse(event.data) || {};
if (data.c !== "r" || data.r !== "h") {
this.watcher['message'].forEach(fn => fn(data));
}
this.sendHeart();
};
this.ws!.onclose = () => {
this.watcher['close'].forEach(fn => fn());
this.doReconnect();
};
this.ws!.onerror = () => { // 默认ws报错就执行重连 todo -- 解决第一次连接失败
this.doReconnect();
};
}
// 监听心跳 重连
sendHeart() {
const that = this;
clearTimeout(this.heartTimeout!);
clearTimeout(this.serverHeartTimeout!);
if (!this.isDestroy) { // ws 已经删除不做心跳重连
this.heartTimeout = setTimeout(() => {
// 发送心跳
if (this.ws && this.ws.readyState === 1) {
this.ws.send(JSON.stringify({ 'c': 'h' }));
}
that.serverHeartTimeout = setTimeout(() => { // 如果超过一定时间还没重置,说明后端断开了
that.doReconnect();
}, that.heartTime);
}, that.heartTime);
}
}
// websocket 发送消息
sendMessage(message: object | string) {
const that = this;
if (message && this.ws) {
try {
this.ws.send(JSON.stringify(message));
} catch (err) {
const timer = setInterval(() => {
if (that.ws && that.ws.readyState === 1) {
that.ws.send(JSON.stringify(message));
clearInterval(timer);
}
}, 200);
const timer01 = setTimeout(() => {
clearTimeout(timer01);
clearInterval(timer);
}, 10000);
}
}
}
// 重连时间间隔控制
_generateInterval(k: number) {
if (this.reconnectIntervalInMilliSeconds > 0) {
return this.reconnectIntervalInMilliSeconds;
}
return Math.min(30, (Math.pow(2, k) - 1)) * 1000;
}
}
const websocket = (options: string | Options) => new WebSocketClass(options);
export {
websocket,
};
typescript封装websocket,监测心跳,断开重连
于 2022-07-12 19:05:24 首次发布