You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

120 lines
3.0 KiB

  1. import { Subject } from "rxjs";
  2. export type SocketState = "open" | "close" | "error";
  3. class WebSocketClient {
  4. private ws: WebSocket | null = null;
  5. private url: string;
  6. private reconnectAttempts: number = -1;
  7. private maxReconnectAttempts: number = 5;
  8. private reconnectInterval: number = 3000;
  9. private dataSub = new Subject<{ func: string; data: Record<string, any> | any[] }>();
  10. get dataOb() {
  11. return this.dataSub.asObservable();
  12. }
  13. private stateSub = new Subject<SocketState>();
  14. get stateOb() {
  15. return this.stateSub.asObservable();
  16. }
  17. constructor(url: string) {
  18. this.url = url;
  19. }
  20. // 连接 WebSocket
  21. connect(): void {
  22. try {
  23. // WebSocket.CONNECTING (0) WebSocket.OPEN (1)
  24. if (this.ws && this.ws.readyState <= 1) {
  25. // 已连接
  26. console.log(`${this.url} 正在连接或已连接,无需重复连接`);
  27. } else {
  28. this.ws = new WebSocket(this.url);
  29. this.bindEvents();
  30. }
  31. localStorage.setItem('wsReadyState', `${this.ws.readyState}`)
  32. } catch (error) {
  33. console.error("WebSocket 连接失败:", error);
  34. this.reconnect();
  35. }
  36. }
  37. // 绑定事件
  38. private bindEvents(): void {
  39. if (!this.ws) return;
  40. // 连接建立时的处理
  41. this.ws.onopen = () => {
  42. console.log("WebSocket 连接已建立");
  43. this.reconnectAttempts = -1; // 重置重连次数
  44. this.stateSub.next("open");
  45. };
  46. // 接收消息的处理
  47. this.ws.onmessage = (event: MessageEvent) => {
  48. try {
  49. const data = JSON.parse(event.data) as { func: string; data: Record<string, any> | any[] };
  50. // console.log("🚀 ~ WebSocketClient ~ bindEvents ~ data:", data);
  51. // if (data.type === "cmd") {
  52. // this.dataSub.next({ type: data.type, data: { ...data.data, success: data.data.status === "D0000" } });
  53. // } else {
  54. this.dataSub.next(data);
  55. // }
  56. } catch (error) {
  57. console.error("消息解析错误:", error);
  58. }
  59. };
  60. this.ws.onclose = () => {
  61. this.stateSub.next("close");
  62. console.log("WebSocket 连接已关闭");
  63. this.reconnect();
  64. };
  65. this.ws.onerror = error => {
  66. this.stateSub.next("error");
  67. console.error("WebSocket 错误:", error);
  68. };
  69. }
  70. // 重连机制
  71. private reconnect(): void {
  72. if (this.reconnectAttempts === -1) {
  73. this.reconnectAttempts = 0;
  74. }
  75. if (this.reconnectAttempts >= this.maxReconnectAttempts) {
  76. console.log("达到最大重连次数,停止重连");
  77. this.reconnectAttempts = -1;
  78. return;
  79. }
  80. setTimeout(() => {
  81. console.log(`尝试第 ${this.reconnectAttempts + 1} 次重连...`);
  82. this.reconnectAttempts++;
  83. this.connect();
  84. }, this.reconnectInterval);
  85. }
  86. // 关闭连接
  87. disconnect(): void {
  88. if (this.ws) {
  89. this.ws.close();
  90. this.ws = null;
  91. }
  92. }
  93. }
  94. const urlSocketMap = new Map<string, WebSocketClient>();
  95. // 导出 WebSocket 客户端
  96. export const createWebSocket = (url: string): WebSocketClient => {
  97. if (urlSocketMap.has(url)) {
  98. return urlSocketMap.get(url)!;
  99. } else {
  100. const client = new WebSocketClient(url);
  101. urlSocketMap.set(url, client);
  102. return client;
  103. }
  104. };
  105. export const sharedWsUrl = `ws://${process.env.REACT_APP_WS_URL}`;