websocket.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. interface SocketOptions {
  2. heartbeatInterval?: number;
  3. reconnectInterval?: number;
  4. maxReconnectAttempts?: number;
  5. isReconnect?: boolean;
  6. uid?: string;
  7. subscribe?: string;
  8. }
  9. class Socket {
  10. url: string;
  11. ws: WebSocket | null = null;
  12. opts: SocketOptions;
  13. reconnectAttempts: number = 0;
  14. listeners: { [key: string]: Function[] } = {};
  15. heartbeatInterval: number | null = null;
  16. constructor(url: string, opts: SocketOptions = {}) {
  17. this.url = url;
  18. this.opts = {
  19. heartbeatInterval: 2 * 1000, // 心跳间隔
  20. reconnectInterval: 2 * 1000, // 重连间隔
  21. maxReconnectAttempts: 99, // 最大重连次数
  22. isReconnect: true, // 是否需要重连
  23. uid: '', // 用户id
  24. subscribe: '', // 订阅的频道
  25. ...opts,
  26. };
  27. this.init();
  28. }
  29. init() {
  30. this.ws = new WebSocket(this.url);
  31. this.ws.onopen = this.onOpen.bind(this);
  32. this.ws.onmessage = this.onMessage.bind(this);
  33. this.ws.onerror = this.onError.bind(this);
  34. this.ws.onclose = this.onClose.bind(this);
  35. }
  36. onOpen(event: Event) {
  37. // this.reconnectAttempts=0;
  38. this.startSubscribe();
  39. this.startHeartbeat();
  40. this.emit('open', event);
  41. }
  42. onMessage(event: MessageEvent) {
  43. this.emit('message', event);
  44. }
  45. onError(event: Event) {
  46. console.error('WebSocket error:', event);
  47. this.emit('error', event);
  48. }
  49. onClose(event: CloseEvent) {
  50. this.stopHeartbeat();
  51. this.emit('close', event);
  52. // @ts-ignore
  53. if (this.opts.isReconnect) {
  54. this.reConnect();
  55. }
  56. }
  57. reConnect() {
  58. // @ts-ignore
  59. if (this.reconnectAttempts < this.opts.maxReconnectAttempts) {
  60. setTimeout(() => {
  61. this.reconnectAttempts++;
  62. this.init();
  63. }, this.opts.reconnectInterval);
  64. } else {
  65. console.error('已到达重连次数最高,请手动刷新重连');
  66. }
  67. }
  68. startHeartbeat() {
  69. if (!this.opts.heartbeatInterval) return;
  70. this.heartbeatInterval = window.setInterval(() => {
  71. if (this.ws?.readyState === WebSocket.OPEN) {
  72. this.send({
  73. id: '',
  74. type: 2,
  75. from: this.opts.uid,
  76. to: 'sys',
  77. timestamps: new Date().getTime(),
  78. body: 'PING',
  79. });
  80. }
  81. }, this.opts.heartbeatInterval);
  82. }
  83. startSubscribe() {
  84. // @ts-ignore
  85. const moreTime = this.opts.heartbeatInterval + 100;
  86. setTimeout(() => {
  87. if (!this.opts.subscribe) return;
  88. this.send({
  89. id: '',
  90. type: 8,
  91. from: this.opts.uid,
  92. to: this.opts.subscribe,
  93. timestamps: new Date().getTime(),
  94. body: 'subscribe',
  95. });
  96. }, moreTime);
  97. }
  98. stopHeartbeat() {
  99. if (this.heartbeatInterval) {
  100. clearInterval(this.heartbeatInterval);
  101. this.heartbeatInterval = null;
  102. }
  103. }
  104. send(data: any) {
  105. if (this.ws?.readyState === WebSocket.OPEN) {
  106. this.ws.send(JSON.stringify(data));
  107. } else {
  108. console.error('WebSocket is not open. Cannot send:', data);
  109. }
  110. }
  111. on(event: string, callback: Function) {
  112. if (!this.listeners[event]) {
  113. this.listeners[event] = [];
  114. }
  115. this.listeners[event].push(callback);
  116. }
  117. off(event: string) {
  118. if (this.listeners[event]) {
  119. delete this.listeners[event];
  120. }
  121. }
  122. emit(event: string, data: any) {
  123. this.listeners[event]?.forEach((callback) => callback(data));
  124. }
  125. close() {
  126. this.opts.isReconnect = false;
  127. this.ws?.close();
  128. this.ws = null;
  129. this.stopHeartbeat();
  130. this.listeners = {};
  131. }
  132. }
  133. export function useSocket(url: string, opts?: SocketOptions) {
  134. const socket = new Socket(url, opts);
  135. return {
  136. socket,
  137. close: socket.close.bind(socket),
  138. send: socket.send.bind(socket),
  139. on: socket.on.bind(socket),
  140. off: socket.off.bind(socket),
  141. };
  142. }