123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- interface SocketOptions {
- heartbeatInterval?: number;
- reconnectInterval?: number;
- maxReconnectAttempts?: number;
- isReconnect?: boolean;
- uid?: string;
- subscribe?: string;
- }
- class Socket {
- url: string;
- ws: WebSocket | null = null;
- opts: SocketOptions;
- reconnectAttempts: number = 0;
- listeners: { [key: string]: Function[] } = {};
- heartbeatInterval: number | null = null;
- constructor(url: string, opts: SocketOptions = {}) {
- this.url = url;
- this.opts = {
- heartbeatInterval: 2 * 1000, // 心跳间隔
- reconnectInterval: 2 * 1000, // 重连间隔
- maxReconnectAttempts: 99, // 最大重连次数
- isReconnect: true, // 是否需要重连
- uid: '', // 用户id
- subscribe: '', // 订阅的频道
- ...opts,
- };
- this.init();
- }
- init() {
- this.ws = new WebSocket(this.url);
- this.ws.onopen = this.onOpen.bind(this);
- this.ws.onmessage = this.onMessage.bind(this);
- this.ws.onerror = this.onError.bind(this);
- this.ws.onclose = this.onClose.bind(this);
- }
- onOpen(event: Event) {
- // this.reconnectAttempts=0;
- this.startSubscribe();
- this.startHeartbeat();
- this.emit('open', event);
- }
- onMessage(event: MessageEvent) {
- this.emit('message', event);
- }
- onError(event: Event) {
- console.error('WebSocket error:', event);
- this.emit('error', event);
- }
- onClose(event: CloseEvent) {
- this.stopHeartbeat();
- this.emit('close', event);
- // @ts-ignore
- if (this.opts.isReconnect) {
- this.reConnect();
- }
- }
- reConnect() {
- // @ts-ignore
- if (this.reconnectAttempts < this.opts.maxReconnectAttempts) {
- setTimeout(() => {
- this.reconnectAttempts++;
- this.init();
- }, this.opts.reconnectInterval);
- } else {
- console.error('已到达重连次数最高,请手动刷新重连');
- }
- }
- startHeartbeat() {
- if (!this.opts.heartbeatInterval) return;
- this.heartbeatInterval = window.setInterval(() => {
- if (this.ws?.readyState === WebSocket.OPEN) {
- this.send({
- id: '',
- type: 2,
- from: this.opts.uid,
- to: 'sys',
- timestamps: new Date().getTime(),
- body: 'PING',
- });
- }
- }, this.opts.heartbeatInterval);
- }
- startSubscribe() {
- // @ts-ignore
- const moreTime = this.opts.heartbeatInterval + 100;
- setTimeout(() => {
- if (!this.opts.subscribe) return;
- this.send({
- id: '',
- type: 8,
- from: this.opts.uid,
- to: this.opts.subscribe,
- timestamps: new Date().getTime(),
- body: 'subscribe',
- });
- }, moreTime);
- }
- stopHeartbeat() {
- if (this.heartbeatInterval) {
- clearInterval(this.heartbeatInterval);
- this.heartbeatInterval = null;
- }
- }
- send(data: any) {
- if (this.ws?.readyState === WebSocket.OPEN) {
- this.ws.send(JSON.stringify(data));
- } else {
- console.error('WebSocket is not open. Cannot send:', data);
- }
- }
- on(event: string, callback: Function) {
- if (!this.listeners[event]) {
- this.listeners[event] = [];
- }
- this.listeners[event].push(callback);
- }
- off(event: string) {
- if (this.listeners[event]) {
- delete this.listeners[event];
- }
- }
- emit(event: string, data: any) {
- this.listeners[event]?.forEach((callback) => callback(data));
- }
- close() {
- this.opts.isReconnect = false;
- this.ws?.close();
- this.ws = null;
- this.stopHeartbeat();
- this.listeners = {};
- }
- }
- export function useSocket(url: string, opts?: SocketOptions) {
- const socket = new Socket(url, opts);
- return {
- socket,
- close: socket.close.bind(socket),
- send: socket.send.bind(socket),
- on: socket.on.bind(socket),
- off: socket.off.bind(socket),
- };
- }
|