@orderly.network/net 1.0.32 → 1.0.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/ws/ws.ts CHANGED
@@ -39,6 +39,9 @@ type Topics = {
39
39
  const defaultMessageFormatter = (message: any) => message.data;
40
40
  const COMMON_ID = "OqdphuyCtYWxwzhxyLLjOWNdFP7sQt8RPWzmb5xY";
41
41
 
42
+ const TIME_OUT = 1000 * 60 * 2;
43
+ const CONNECT_LIMIT = 5;
44
+
42
45
  export class WS {
43
46
  private publicSocket!: WebSocket;
44
47
  private privateSocket?: WebSocket;
@@ -57,6 +60,12 @@ export class WS {
57
60
  private _eventHandlers: Map<string, Topics> = new Map();
58
61
  private _eventPrivateHandlers: Map<string, Topics> = new Map();
59
62
 
63
+ private _publicHeartbeatTime?: number;
64
+ private _privateHeartbeatTime?: number;
65
+
66
+ private _publicRetryCount: number = 0;
67
+ private _privateRetryCount: number = 0;
68
+
60
69
  constructor(private options: WSOptions) {
61
70
  this.createPublicSC(options);
62
71
 
@@ -69,23 +78,93 @@ export class WS {
69
78
 
70
79
  private bindEvents() {
71
80
  if (typeof document !== "undefined") {
72
- document.addEventListener("visibilitychange", this.onVisibilityChange);
81
+ document.addEventListener(
82
+ "visibilitychange",
83
+ this.onVisibilityChange.bind(this)
84
+ );
73
85
  }
74
86
 
75
87
  if (typeof window !== "undefined") {
76
- window.addEventListener("online", this.onNetworkStatusChange);
77
- window.addEventListener("offline", this.onNetworkStatusChange);
88
+ window.addEventListener("online", this.onNetworkStatusChange.bind(this));
89
+ // window.addEventListener("offline", this.onNetworkStatusChange);
78
90
  }
79
91
  }
80
92
 
81
93
  private onVisibilityChange() {
82
94
  console.log("👀👀 document visibility 👀👀", document.visibilityState);
83
- if (document.visibilityState) {
95
+ if (document.visibilityState === "visible") {
96
+ this.checkSocketStatus();
84
97
  }
98
+ // else {
99
+ // this.publicSocket.close();
100
+ // this.privateSocket?.close();
101
+ // }
85
102
  }
86
103
 
87
104
  private onNetworkStatusChange() {
88
105
  console.log("👀👀 network status 👀👀", navigator.onLine);
106
+
107
+ if (navigator.onLine) {
108
+ this.checkSocketStatus();
109
+ }
110
+ }
111
+
112
+ /**
113
+ * 判断当前连接状态,
114
+ * 1、如果已断开则重连
115
+ * 2、如果太久没有收到消息,则主动断开,并重连
116
+ * 3、从后台返回、网络状态变化时,都走以下流程
117
+ */
118
+ private checkSocketStatus() {
119
+ const now = Date.now();
120
+
121
+ console.log(
122
+ "👀👀 checkNetworkStatus 👀👀",
123
+ this._publicHeartbeatTime,
124
+ this._privateHeartbeatTime,
125
+ now,
126
+ this.publicSocket.readyState,
127
+ this.privateSocket?.readyState
128
+ );
129
+
130
+ // check the last time
131
+ // 如果容器不可见,则不做处理
132
+ if (document.visibilityState !== "visible") return;
133
+ // 如果网络不可用,则不做处理
134
+ if (!navigator.onLine) return;
135
+
136
+ // 如果已断开,则重连
137
+ // public
138
+ if (!this.publicIsReconnecting) {
139
+ if (this.publicSocket.readyState === WebSocket.CLOSED) {
140
+ this.reconnectPublic();
141
+ } else {
142
+ console.log(
143
+ "***********************",
144
+ this.publicSocket.readyState,
145
+ now - this._publicHeartbeatTime!
146
+ );
147
+ if (now - this._publicHeartbeatTime! > TIME_OUT) {
148
+ //unsubscribe all public topic
149
+ this.publicSocket.close();
150
+ }
151
+ }
152
+ }
153
+
154
+ if (!this.privateIsReconnecting) {
155
+ // private
156
+ if (this.privateSocket?.readyState === WebSocket.CLOSED) {
157
+ this.reconnectPrivate();
158
+ } else {
159
+ if (
160
+ this._privateHeartbeatTime &&
161
+ now - this._privateHeartbeatTime! > TIME_OUT
162
+ ) {
163
+ // unsubscribe all private topic
164
+ this.privateSocket?.close();
165
+ }
166
+ }
167
+ }
89
168
  }
90
169
 
91
170
  public openPrivate(accountId: string) {
@@ -108,17 +187,22 @@ export class WS {
108
187
  }
109
188
 
110
189
  private createPublicSC(options: WSOptions) {
190
+ console.log("open public webSocket ---->>>>");
191
+ if (this.publicSocket && this.publicSocket.readyState === WebSocket.OPEN)
192
+ return;
111
193
  this.publicSocket = new WebSocket(
112
194
  `${this.options.publicUrl}/ws/stream/${COMMON_ID}`
113
195
  );
114
196
  this.publicSocket.onopen = this.onOpen.bind(this);
115
197
  this.publicSocket.onmessage = this.onPublicMessage.bind(this);
116
- this.publicSocket.onclose = this.onClose.bind(this);
117
- this.publicSocket.onerror = this.onError.bind(this);
198
+ this.publicSocket.onclose = this.onPublicClose.bind(this);
199
+ this.publicSocket.onerror = this.onPublicError.bind(this);
118
200
  }
119
201
 
120
202
  private createPrivateSC(options: WSOptions) {
121
- console.log("to open private webSocket ---->>>>");
203
+ console.log("open private webSocket ---->>>>");
204
+ if (this.privateSocket && this.privateSocket.readyState === WebSocket.OPEN)
205
+ return;
122
206
 
123
207
  this.options = options;
124
208
 
@@ -127,12 +211,13 @@ export class WS {
127
211
  );
128
212
  this.privateSocket.onopen = this.onPrivateOpen.bind(this);
129
213
  this.privateSocket.onmessage = this.onPrivateMessage.bind(this);
130
- // this.privateSocket.onclose = this.onClose.bind(this);
214
+ this.privateSocket.onclose = this.onPrivateClose.bind(this);
131
215
  this.privateSocket.onerror = this.onPrivateError.bind(this);
132
216
  }
133
217
 
134
218
  private onOpen(event: Event) {
135
- console.log("WebSocket connection opened:");
219
+ console.log("Public WebSocket connection opened");
220
+
136
221
  // console.log(this._pendingPublicSubscribe);
137
222
  if (this._pendingPublicSubscribe.length > 0) {
138
223
  this._pendingPublicSubscribe.forEach(([params, cb, isOnce]) => {
@@ -142,13 +227,15 @@ export class WS {
142
227
  }
143
228
 
144
229
  this.publicIsReconnecting = false;
230
+ this._publicRetryCount = 0;
145
231
  }
146
232
 
147
233
  private onPrivateOpen(event: Event) {
148
- console.log("Private WebSocket connection opened:");
234
+ console.log("Private WebSocket connection opened");
149
235
  //auth
150
236
  this.authenticate(this.options.accountId!);
151
237
  this.privateIsReconnecting = false;
238
+ this._privateRetryCount = 0;
152
239
  }
153
240
 
154
241
  private onMessage(
@@ -184,11 +271,13 @@ export class WS {
184
271
  }
185
272
  });
186
273
 
187
- if (eventhandler.isOnce) {
188
- handlerMap.delete(topicKey);
189
- }
274
+ // 延时删除,在重连时还需要用到
275
+ // if (eventhandler.isOnce) {
276
+ // handlerMap.delete(topicKey);
277
+ // }
190
278
  }
191
279
  }
280
+
192
281
  // console.log("WebSocket message received:", message);
193
282
  } catch (e) {
194
283
  console.log("WebSocket message received:", e, event.data);
@@ -199,10 +288,14 @@ export class WS {
199
288
 
200
289
  private onPublicMessage(event: MessageEvent) {
201
290
  this.onMessage(event, this.publicSocket, this._eventHandlers);
291
+ // 更新最后收到消息的时间
292
+ this._publicHeartbeatTime = Date.now();
202
293
  }
203
294
 
204
295
  private onPrivateMessage(event: MessageEvent) {
205
296
  this.onMessage(event, this.privateSocket!, this._eventPrivateHandlers);
297
+ // 更新最后收到消息的时间
298
+ this._privateHeartbeatTime = Date.now();
206
299
  }
207
300
 
208
301
  private handlePendingPrivateTopic() {
@@ -214,31 +307,83 @@ export class WS {
214
307
  }
215
308
  }
216
309
 
217
- private onClose(event: CloseEvent) {
218
- console.log("WebSocket connection closed:", event.reason);
310
+ private onPublicClose(event: CloseEvent) {
311
+ console.log("public socket is closed");
312
+
313
+ // move handler to pending
314
+ this._eventHandlers.forEach((value, key) => {
315
+ value.callback.forEach((cb) => {
316
+ this._pendingPublicSubscribe.push([value.params, cb, value.isOnce]);
317
+ });
318
+
319
+ this._eventHandlers.delete(key);
320
+ });
321
+
322
+ setTimeout(() => this.checkSocketStatus(), 0);
219
323
  }
220
324
 
221
- private onError(event: Event) {
222
- console.error("WebSocket error:", event);
325
+ private onPrivateClose(event: CloseEvent) {
326
+ console.log("private socket is closed");
327
+ if (this.privateIsReconnecting) return;
328
+ this._eventPrivateHandlers.forEach((value, key) => {
329
+ console.log(value);
223
330
 
224
- this._eventHandlers.forEach((value, key) => {
225
- if (!value.isPrivate) {
226
- this._pendingPublicSubscribe.push([value.params, value.callback]);
227
- this._eventHandlers.delete(key);
228
- }
331
+ value.callback.forEach((cb) => {
332
+ this._pendingPrivateSubscribe.push([value.params, cb, value.isOnce]);
333
+ });
334
+
335
+ this._eventPrivateHandlers.delete(key);
229
336
  });
337
+ this.authenticated = false;
230
338
 
231
- this.reconnectPublic();
339
+ setTimeout(() => this.checkSocketStatus(), 0);
340
+ }
341
+
342
+ private onPublicError(event: Event) {
343
+ console.error("public WebSocket error:", event);
344
+ this.publicIsReconnecting = false;
345
+
346
+ if (this.publicSocket.readyState === WebSocket.OPEN) {
347
+ this.publicSocket.close();
348
+ } else {
349
+ // retry connect
350
+ if (this._publicRetryCount > CONNECT_LIMIT) return;
351
+ setTimeout(() => {
352
+ console.log("retry connect: %s", this._publicRetryCount);
353
+ // this.createPublicSC(this.options);
354
+ this.reconnectPublic();
355
+ this._publicRetryCount++;
356
+ }, this._publicRetryCount * 1000);
357
+ }
358
+
359
+ this.errorBoardscast(event, this._eventHandlers);
232
360
  }
233
361
 
234
362
  private onPrivateError(event: Event) {
235
363
  console.error("Private WebSocket error:", event);
364
+ this.privateIsReconnecting = false;
236
365
 
237
- this._eventHandlers.forEach((value, key) => {
238
- if (value.isPrivate) {
239
- this._pendingPrivateSubscribe.push([value.params, value.callback]);
240
- this._eventHandlers.delete(key);
241
- }
366
+ if (this.privateSocket?.readyState === WebSocket.OPEN) {
367
+ this.privateSocket.close();
368
+ } else {
369
+ // retry connect
370
+ if (this._privateRetryCount > CONNECT_LIMIT) return;
371
+ setTimeout(() => {
372
+ console.log("retry connect: %s", this._privateRetryCount);
373
+ // this.createPublicSC(this.options);
374
+ this.reconnectPrivate();
375
+ this._privateRetryCount++;
376
+ }, this._privateRetryCount * 1000);
377
+ }
378
+
379
+ this.errorBoardscast(event, this._eventPrivateHandlers);
380
+ }
381
+
382
+ private errorBoardscast(error: any, eventHandlers: Map<string, Topics>) {
383
+ eventHandlers.forEach((value) => {
384
+ value.callback.forEach((cb) => {
385
+ cb.onError?.(error);
386
+ });
242
387
  });
243
388
  }
244
389
 
@@ -368,7 +513,13 @@ export class WS {
368
513
  });
369
514
  this.publicSocket.send(JSON.stringify(subscribeMessage));
370
515
  } else {
371
- handler.callback.push(callbacks);
516
+ // 是否once,如果是once,则替换掉之前的callback
517
+ if (once) {
518
+ handler.callback = [callbacks];
519
+ this.publicSocket.send(JSON.stringify(subscribeMessage));
520
+ } else {
521
+ handler.callback.push(callbacks);
522
+ }
372
523
  }
373
524
 
374
525
  // this._subscriptionPublicTopics.push({params, cb: [cb]});
@@ -487,12 +638,27 @@ export class WS {
487
638
  private reconnectPublic() {
488
639
  if (this.publicIsReconnecting) return;
489
640
  this.publicIsReconnecting = true;
490
- console.log(`Reconnecting in ${this.reconnectInterval / 1000} seconds...`);
641
+ console.log(
642
+ `Reconnecting public in ${this.reconnectInterval / 1000} seconds...`
643
+ );
491
644
  window.setTimeout(() => {
492
- console.log("Reconnecting...");
493
- // this.publicIsReconnecting = false;
645
+ console.log("Public Reconnecting...");
494
646
 
495
647
  this.createPublicSC(this.options);
496
648
  }, this.reconnectInterval);
497
649
  }
650
+
651
+ private reconnectPrivate() {
652
+ if (!this.options.accountId) return;
653
+ if (this.privateIsReconnecting) return;
654
+ this.privateIsReconnecting = true;
655
+ console.log(
656
+ `Reconnecting private in ${this.reconnectInterval / 1000} seconds...`
657
+ );
658
+ window.setTimeout(() => {
659
+ console.log("Private Reconnecting...");
660
+
661
+ this.createPrivateSC(this.options);
662
+ }, this.reconnectInterval);
663
+ }
498
664
  }