websocket.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import env from "../config/env.js";
  2. class WebSocketService {
  3. constructor() {
  4. this.socketTask = null;
  5. this.isConnected = false;
  6. this.reconnectAttempts = 0;
  7. this.maxReconnectAttempts = 5;
  8. this.reconnectInterval = 3000; // 3秒重连
  9. this.heartbeatInterval = 60000; // 60秒发送一次心跳
  10. this.heartbeatTimer = null;
  11. this.reconnectTimer = null;
  12. }
  13. async connect() {
  14. // 先关闭之前的连接
  15. // await this.disconnect();
  16. const userId = uni.getStorageSync("userInfo").userId;
  17. const baseUrl = env.baseUrl.replace("https://", "");
  18. const wsUrl = `wss://${baseUrl}/ws?userId=${userId}`;
  19. console.log("Attempting to connect to WebSocket:", wsUrl);
  20. return new Promise((resolve, reject) => {
  21. try {
  22. this.socketTask = uni.connectSocket({
  23. url: wsUrl,
  24. complete: () => {
  25. console.log("WebSocket connection attempt completed");
  26. },
  27. });
  28. if (!this.socketTask) {
  29. console.error("Failed to create socket task");
  30. reject(new Error("Failed to create socket task"));
  31. return;
  32. }
  33. // 监听连接打开
  34. this.socketTask.onOpen(() => {
  35. console.log("WebSocket connected successfully");
  36. this.isConnected = true;
  37. this.reconnectAttempts = 0;
  38. // 启动心跳
  39. this.startHeartbeat();
  40. resolve();
  41. });
  42. // 监听消息
  43. this.socketTask.onMessage((res) => {
  44. try {
  45. // 先解析 JSON 字符串
  46. const data = JSON.parse(res.data);
  47. // 判断是否是心跳响应
  48. if (data.message === "PONG") {
  49. return;
  50. }
  51. switch (data.type) {
  52. case "INIT":
  53. uni.$emit("init", data);
  54. break;
  55. case "RUNNING":
  56. uni.$emit("newMessage", data);
  57. break;
  58. case "NEW_RECORD":
  59. uni.$emit("newRecord", data);
  60. break;
  61. default:
  62. console.log("Unknown message type:", data.type);
  63. }
  64. } catch (error) {
  65. console.error("Failed to parse WebSocket message:", error);
  66. }
  67. });
  68. // 监听连接关闭
  69. this.socketTask.onClose((res) => {
  70. console.log("WebSocket closed:", res);
  71. this.isConnected = false;
  72. this.stopHeartbeat();
  73. if (
  74. !res.wasClean &&
  75. this.reconnectAttempts < this.maxReconnectAttempts
  76. ) {
  77. this.reconnectAttempts++;
  78. console.log(
  79. `Attempting to reconnect (${this.reconnectAttempts}/${this.maxReconnectAttempts})`
  80. );
  81. this.reconnectTimer = setTimeout(
  82. () => this.connect(),
  83. this.reconnectInterval
  84. );
  85. }
  86. });
  87. // 监听错误
  88. this.socketTask.onError((error) => {
  89. console.error("WebSocket error:", error);
  90. this.isConnected = false;
  91. this.stopHeartbeat();
  92. reject(error);
  93. });
  94. } catch (error) {
  95. console.error("Failed to create WebSocket connection:", error);
  96. reject(error);
  97. }
  98. });
  99. }
  100. // 启动心跳
  101. startHeartbeat() {
  102. this.stopHeartbeat(); // 先清除可能存在的定时器
  103. this.heartbeatTimer = setInterval(() => {
  104. if (this.isConnected) {
  105. this.send({ type: "PING" });
  106. }
  107. }, this.heartbeatInterval);
  108. }
  109. // 停止心跳
  110. stopHeartbeat() {
  111. if (this.heartbeatTimer) {
  112. clearInterval(this.heartbeatTimer);
  113. this.heartbeatTimer = null;
  114. }
  115. }
  116. async disconnect() {
  117. this.stopHeartbeat();
  118. if (this.reconnectTimer) {
  119. clearTimeout(this.reconnectTimer);
  120. this.reconnectTimer = null;
  121. }
  122. return new Promise((resolve) => {
  123. if (this.socketTask) {
  124. this.socketTask.close({
  125. success: () => {
  126. console.log("WebSocket disconnected successfully");
  127. this.socketTask = null;
  128. this.isConnected = false;
  129. resolve();
  130. },
  131. fail: (error) => {
  132. console.error("Failed to disconnect WebSocket:", error);
  133. this.socketTask = null;
  134. this.isConnected = false;
  135. resolve();
  136. },
  137. });
  138. } else {
  139. resolve();
  140. }
  141. });
  142. }
  143. async send(message) {
  144. if (!this.isConnected || !this.socketTask) {
  145. console.error("WebSocket is not connected");
  146. return false;
  147. }
  148. return new Promise((resolve, reject) => {
  149. this.socketTask.send({
  150. data: JSON.stringify(message),
  151. success: () => {
  152. console.log("Message sent successfully");
  153. resolve(true);
  154. },
  155. fail: (error) => {
  156. console.error("Failed to send message:", error);
  157. reject(error);
  158. },
  159. });
  160. });
  161. }
  162. }
  163. // 创建单例
  164. const websocketService = new WebSocketService();
  165. export default websocketService;