websocket.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. class WebSocketClient {
  2. constructor() {
  3. this.ws = null;
  4. this.isConnected = false;
  5. this.reconnectAttempts = 0;
  6. this.maxReconnectAttempts = 5;
  7. this.reconnectInterval = 3000;
  8. this.messageQueue = [];
  9. this.onMessageCallback = null;
  10. this.onErrorCallback = null;
  11. this.onCloseCallback = null;
  12. this.onOpenCallback = null;
  13. this.url = null;
  14. this.heartbeatTimer = null;
  15. this.heartbeatInterval = 20000;
  16. this.heartbeatMessage = 'PING';
  17. this.lastHeartbeatTime = 0;
  18. this.heartbeatTimeout = 30000;
  19. this.connectionPromise = null;
  20. }
  21. connect(url, params = {}) {
  22. // 如果已经有正在进行的连接,返回该连接的Promise
  23. if (this.connectionPromise) {
  24. return this.connectionPromise;
  25. }
  26. this.connectionPromise = new Promise((resolve, reject) => {
  27. try {
  28. const queryString = Object.keys(params)
  29. .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
  30. .join('&');
  31. console.log(url,);
  32. const fullUrl = `${url}${queryString ? '?' + queryString : ''}`;
  33. this.url = fullUrl;
  34. this.ws = uni.connectSocket({
  35. url: fullUrl,
  36. success: () => {
  37. console.log('WebSocket连接成功');
  38. },
  39. fail: (error) => {
  40. console.error('WebSocket连接失败:', error);
  41. this.connectionPromise = null;
  42. reject(error);
  43. }
  44. });
  45. this.ws.onOpen(() => {
  46. console.log('WebSocket已打开');
  47. this.isConnected = true;
  48. this.reconnectAttempts = 0;
  49. this.startHeartbeat();
  50. // 发送队列中的消息
  51. while (this.messageQueue.length > 0) {
  52. const message = this.messageQueue.shift();
  53. this.send(message);
  54. }
  55. resolve();
  56. if (this.onOpenCallback) {
  57. this.onOpenCallback();
  58. }
  59. });
  60. this.ws.onMessage((res) => {
  61. if (res.data === 'PONG'||res.data === 'PING') {
  62. this.lastHeartbeatTime = Date.now();
  63. return;
  64. }
  65. if (this.onMessageCallback) {
  66. this.onMessageCallback(res.data);
  67. }
  68. });
  69. this.ws.onError((error) => {
  70. console.error('WebSocket错误:', error);
  71. this.isConnected = false;
  72. this.stopHeartbeat();
  73. if (this.onErrorCallback) {
  74. this.onErrorCallback(error);
  75. }
  76. this.connectionPromise = null;
  77. this.reconnect();
  78. });
  79. this.ws.onClose(() => {
  80. console.log('WebSocket已关闭');
  81. this.isConnected = false;
  82. this.stopHeartbeat();
  83. if (this.onCloseCallback) {
  84. this.onCloseCallback();
  85. }
  86. this.connectionPromise = null;
  87. this.reconnect();
  88. });
  89. } catch (error) {
  90. console.error('WebSocket初始化错误:', error);
  91. this.connectionPromise = null;
  92. reject(error);
  93. }
  94. });
  95. return this.connectionPromise;
  96. }
  97. startHeartbeat() {
  98. this.stopHeartbeat();
  99. this.lastHeartbeatTime = Date.now();
  100. this.heartbeatTimer = setInterval(() => {
  101. if (this.isConnected) {
  102. // 检查上次心跳响应时间
  103. if (Date.now() - this.lastHeartbeatTime > this.heartbeatTimeout) {
  104. console.log('心跳超时,准备重连');
  105. this.reconnect();
  106. return;
  107. }
  108. // 发送心跳
  109. this.send(this.heartbeatMessage);
  110. } else {
  111. this.stopHeartbeat();
  112. }
  113. }, this.heartbeatInterval);
  114. }
  115. stopHeartbeat() {
  116. if (this.heartbeatTimer) {
  117. clearInterval(this.heartbeatTimer);
  118. this.heartbeatTimer = null;
  119. }
  120. }
  121. reconnect() {
  122. if (this.reconnectAttempts < this.maxReconnectAttempts) {
  123. this.reconnectAttempts++;
  124. console.log(`尝试重新连接 (${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
  125. setTimeout(() => {
  126. this.connect(this.url); // 使用保存的URL进行重新连接
  127. }, this.reconnectInterval);
  128. }
  129. }
  130. async send(message) {
  131. // 如果未连接,将消息加入队列并尝试连接
  132. if (!this.isConnected) {
  133. this.messageQueue.push(message);
  134. if (!this.connectionPromise) {
  135. try {
  136. await this.connect(this.url);
  137. } catch (error) {
  138. console.error('发送消息时连接失败:', error);
  139. return;
  140. }
  141. }
  142. return;
  143. }
  144. // 确保连接已建立
  145. if (this.connectionPromise) {
  146. try {
  147. await this.connectionPromise;
  148. } catch (error) {
  149. console.error('等待连接建立失败:', error);
  150. return;
  151. }
  152. }
  153. // 发送消息
  154. this.ws.send({
  155. data: message,
  156. fail: (error) => {
  157. console.error('发送消息失败:', error);
  158. this.messageQueue.push(message);
  159. // 如果发送失败,可能是连接已断开,尝试重连
  160. if (!this.isConnected) {
  161. this.reconnect();
  162. }
  163. }
  164. });
  165. }
  166. close() {
  167. this.stopHeartbeat();
  168. if (this.ws) {
  169. this.ws.close();
  170. this.ws = null;
  171. this.isConnected = false;
  172. }
  173. }
  174. onMessage(callback) {
  175. this.onMessageCallback = callback;
  176. }
  177. onError(callback) {
  178. this.onErrorCallback = callback;
  179. }
  180. onClose(callback) {
  181. this.onCloseCallback = callback;
  182. }
  183. onOpen(callback) {
  184. this.onOpenCallback = callback;
  185. }
  186. }
  187. export default new WebSocketClient();