websocket.js 4.6 KB

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