From ed2b0a2bcfcc3ff353d40705e841a4520b661461 Mon Sep 17 00:00:00 2001 From: Jonah <47046556+jwbonner@users.noreply.github.com> Date: Sun, 29 Oct 2023 12:32:42 -0400 Subject: [PATCH] Improve NT reliability when reconnecting WS timeouts are unpredictable and leave old connections hanging, so now HTTP is used for aliveness checking after the WS disconnects or times out --- src/hub/dataSources/NT4.ts | 82 +++++++++++++++++++++++--------------- www/hub.html | 4 +- 2 files changed, 51 insertions(+), 35 deletions(-) diff --git a/src/hub/dataSources/NT4.ts b/src/hub/dataSources/NT4.ts index 47073ec1..8a42783f 100644 --- a/src/hub/dataSources/NT4.ts +++ b/src/hub/dataSources/NT4.ts @@ -85,7 +85,7 @@ export class NT4_Topic { } export class NT4_Client { - private RECONNECT_TIMEOUT_MS = 250; + private PORT = 5810; private RTT_PERIOD_MS_V40 = 1000; private RTT_PERIOD_MS_V41 = 250; private TIMEOUT_MS_V40 = 5000; @@ -143,6 +143,35 @@ export class NT4_Client { this.onNewTopicData = onNewTopicData; this.onConnect = onConnect; this.onDisconnect = onDisconnect; + + this.timestampInterval = setInterval(() => { + if (this.rttWs === null) { + // Use v4.0 timeout (RTT ws not created) + this.ws_sendTimestamp(); + } + }, this.RTT_PERIOD_MS_V40); + this.rttWsTimestampInterval = setInterval(() => { + if (this.rttWs !== null) { + // Use v4.1 timeout (RTT ws was created) + this.ws_sendTimestamp(); + } + }, this.RTT_PERIOD_MS_V41); + } + + private async connectOnAlive() { + let result: Response | null = null; + let requestStart = new Date().getTime(); + try { + result = await fetch("http://" + this.serverBaseAddr + ":" + this.PORT.toString(), { + signal: AbortSignal.timeout(250) + }); + } catch (err) {} + if (result === null || !result.ok) { + let requestLength = new Date().getTime() - requestStart; + setTimeout(() => this.connectOnAlive(), 350 - requestLength); + } else { + this.ws_connect(); + } } ////////////////////////////////////////////////////////////// @@ -152,19 +181,7 @@ export class NT4_Client { connect() { if (!this.serverConnectionRequested) { this.serverConnectionRequested = true; - this.ws_connect(); - this.timestampInterval = setInterval(() => { - if (this.rttWs === null) { - // Use v4.0 timeout (RTT ws not created) - this.ws_sendTimestamp(); - } - }, this.RTT_PERIOD_MS_V40); - this.rttWsTimestampInterval = setInterval(() => { - if (this.rttWs !== null) { - // Use v4.1 timeout (RTT ws was created) - this.ws_sendTimestamp(); - } - }, this.RTT_PERIOD_MS_V41); + this.connectOnAlive(); } } @@ -172,9 +189,8 @@ export class NT4_Client { disconnect() { if (this.serverConnectionRequested) { this.serverConnectionRequested = false; - if (this.serverConnectionActive) { - this.ws?.close(); - this.rttWs?.close(); + if (this.serverConnectionActive && this.ws) { + this.ws_onClose(new CloseEvent("close"), this.ws); } if (this.timestampInterval !== null) { clearInterval(this.timestampInterval); @@ -460,7 +476,9 @@ export class NT4_Client { } } - private ws_onClose(event: CloseEvent, reconnect = true) { + private ws_onClose(event: CloseEvent, source: WebSocket) { + if (source !== this.ws) return; + // Stop server communication this.ws?.close(); this.rttWs?.close(); @@ -479,15 +497,19 @@ export class NT4_Client { // Clear out any local cache of server state this.serverTopics.clear(); + // Print reason if (event.reason !== "") { console.log("[NT4] Socket is closed: ", event.reason); } - if (reconnect && this.serverConnectionRequested) { - setTimeout(() => this.ws_connect(), this.RECONNECT_TIMEOUT_MS); + + // Reconnect when alive again + if (this.serverConnectionRequested) { + this.connectOnAlive(); } } - private ws_onError() { + private ws_onError(source: WebSocket) { + if (source !== this.ws) return; this.ws?.close(); this.rttWs?.close(); } @@ -609,20 +631,14 @@ export class NT4_Client { const timeout = this.rttWs === null ? this.TIMEOUT_MS_V40 : this.TIMEOUT_MS_V41; this.disconnectTimeout = setTimeout(() => { console.log("[NT4] No data for " + timeout.toString() + "ms, closing"); - - // Close sockets and act like the connection was closed normally. If - // communication was lost another close event will be emitted on - // reconnection once the close message goes through. The second event - // will trigger the reconnect, so don't do it here. - this.ws_onClose(new CloseEvent("close"), false); + if (this.ws) { + this.ws_onClose(new CloseEvent("close"), this.ws); + } }, timeout); } private ws_connect(rttWs = false) { - let port = 5810; - let prefix = "ws://"; - - this.serverAddr = prefix + this.serverBaseAddr + ":" + port.toString() + "/nt/" + this.appName; + this.serverAddr = "ws://" + this.serverBaseAddr + ":" + this.PORT.toString() + "/nt/" + this.appName; let ws = new WebSocket( this.serverAddr, @@ -636,10 +652,10 @@ export class NT4_Client { ws.binaryType = "arraybuffer"; ws.addEventListener("open", () => this.ws_onOpen(ws)); ws.addEventListener("message", (event: MessageEvent) => this.ws_onMessage(event, rttWs)); - ws.addEventListener("error", () => this.ws_onError()); if (!rttWs) { // Don't run two callbacks when on normal and RTT close - ws.addEventListener("close", (event: CloseEvent) => this.ws_onClose(event)); + ws.addEventListener("error", () => this.ws_onError(ws)); + ws.addEventListener("close", (event: CloseEvent) => this.ws_onClose(event, ws)); } } diff --git a/www/hub.html b/www/hub.html index c22e4bdc..dfd13d17 100644 --- a/www/hub.html +++ b/www/hub.html @@ -4,11 +4,11 @@