WebSocket: So stellen Sie die Verbindung nach dem Tod automatisch wieder her

74
var ws = new WebSocket('ws://localhost:8080');
ws.onopen = function () {
  ws.send(JSON.stringify({
      .... some message the I must send when I connect ....
  }));

};

ws.onmessage = function (e) {
  console.log('Got a message')
  console.log(e.data);
};

ws.onclose = function(e) {  
  console.log('socket closed try again'); 

}

ws.onerror = function(err) {
  console.error(err)
};

Wenn ich mich zum ersten Mal mit dem Socket verbinde, muss ich zuerst eine Nachricht an den Server senden, um mich zu authentifizieren und Kanäle zu abonnieren.

Das Problem, das ich habe, ist, dass der Socket-Server manchmal unzuverlässig ist und die onerrorund oncloseEreignisse des 'ws'Objekts auslöst .

Frage: Was ist ein gutes Entwurfsmuster, das es mir ermöglicht, bei jedem Schließen des Sockets oder bei Auftreten eines Fehlers 10 Sekunden zu warten und dann erneut eine Verbindung zum Socket-Server herzustellen (und die ursprüngliche Nachricht erneut an den Server zu senden)?

Samol
quelle

Antworten:

131

Hier ist, was ich am Ende hatte. Es funktioniert für meine Zwecke.

function connect() {
  var ws = new WebSocket('ws://localhost:8080');
  ws.onopen = function() {
    // subscribe to some channels
    ws.send(JSON.stringify({
        //.... some message the I must send when I connect ....
    }));
  };

  ws.onmessage = function(e) {
    console.log('Message:', e.data);
  };

  ws.onclose = function(e) {
    console.log('Socket is closed. Reconnect will be attempted in 1 second.', e.reason);
    setTimeout(function() {
      connect();
    }, 1000);
  };

  ws.onerror = function(err) {
    console.error('Socket encountered error: ', err.message, 'Closing socket');
    ws.close();
  };
}

connect();
Samol
quelle
2
Stellt das wieder eine Verbindung zu demselben Websocket her, mit dem es zuvor verbunden war? Da ich die Websocket-ID zum Senden von Nachrichten verwende, aber wenn es eine neue Websocket-ID gibt, ist es schwierig, Nachrichten an ein bestimmtes System zu senden.
Vishnu YS
17
@AlexanderDunaev, das Timeout wird hauptsächlich hinzugefügt, um eine zu aggressive erneute Verbindung zu vermeiden, wenn der Server nicht verfügbar ist, dh ein defektes Netzwerk oder das Herunterfahren des lokalen Debug-Servers. Aber im Allgemeinen denke ich, dass eine sofortige Wiederverbindung, gefolgt von einer exponentiell wachsenden Wartezeit für die Wiederverbindung, eine etwas bessere Wahl wäre als eine feste Wartezeit von 1 Sekunde.
user658991
12
Was passiert mit der Websocket-Instanz, wenn die Verbindung geschlossen wird? Wird Müll gesammelt oder baut der Browser einen Stapel nicht verwendeter Objekte auf?
Knobo
7
setTimeout (connect, 1000) ist eine präzisere und ressourceneffizientere Methode, um die Wiederverbindung zu verzögern. Verwenden Sie auch setTimeout (connect, Math.min (10000, timeout + = timeout)) und setzen Sie das Timeout vor der ersten Verbindung und nach jeder erfolgreichen Verbindung auf 250 zurück. Auf diese Weise wird durch Fehlerbedingungen während der Verbindung ein Backoff hinzugefügt, die Verbindung wird jedoch schnell wiederhergestellt, wenn es sich um eine einmalige Fehlersituation handelt. 250.500.1000.2000.4000.8000.10000.10000 ms Verzögerungen sind weniger aggressiv, reagieren jedoch schneller als 1000.1000 , 1000 ms
synchronisiert
3
Das Problem, das ich bei diesem Code sehe, ist, dass wenn die Verbindung geschlossen wird und wir versuchen, die Verbindung erneut zu öffnen, und dies fehlschlägt, wir niemals einen erneuten Versuch ausführen werden.
Michael Connor
2

Dies hat bei mir funktioniert setInterval, da die Clientverbindung unterbrochen werden kann.

ngOnInit(): void {
    if (window.location.protocol.includes('https')) {
        this.protocol = 'wss';
    }

    this.listenChanges();
}


listenChanges(): void {
    this.socket = new WebSocket(`${this.protocol}://${window.location.host}/v1.0/your/url`);

    this.socket.onmessage = (event): void => {
        // your subscription stuff
        this.store.dispatch(someAction);
    };

    this.socket.onerror = (): void => {
        this.socket.close();
    };


    this.socket.onopen = (): void => {
        clearInterval(this.timerId);

        this.socket.onclose = (): void => {
            this.timerId = setInterval(() => {
                this.listenChanges();
            }, 10000);
        };
    };
}

Vergessen Sie nicht anzurufen, clearIntervalwenn die Steckdose geöffnet wurde.

Bulat
quelle
2

AKTUALISIERTE Antwort:

Endlich (wenn Sie kein Java verwenden) habe ich festgestellt, dass Sie besser Ihre eigene "Ping / Pong" -Strategie implementieren sollten. (Wenn Sie Java verwenden, werfen Sie bitte einen Blick auf Ping / Pong "Aktionstyp", ich erinnere mich nicht sehr klar ...)

  1. Der Client hat alle 5 Sekunden einen "Ping" an den Server gesendet.
  2. Der Server sollte dem Client ein "Pong" zurückgeben, sobald er "Ping" erhält.
  3. Der Client sollte den Server erneut verbinden, wenn er innerhalb von 5 Sekunden kein "Pong" erhält.

Verlassen Sie sich nicht auf Bibliotheken von Drittanbietern.

WARNUNG: Verwenden Sie diese Werkzeuge NICHT:

  1. Überprüfen Sie, ob das Netzwerk verfügbar ist: https://github.com/hubspot/offline
  2. Zum erneuten Verbinden: https://github.com/joewalnes/reconnecting-websocket
Siwei Shen 申思维
quelle
1
Die Github-Bibliothek github.com/joewalnes/reconnecting-websocket funktioniert tatsächlich als einfaches Drop-In für new WebSocket()eine einfache Verbindung. Ich weiß, dass diese Antwort im Allgemeinen etwas falsch ist, aber der Einfachheit halber funktioniert die Verwendung der hier erwähnten Javascript-Bibliothek.
TechnicalChaos
Ja, du hast recht ! Verwenden Sie diese 2 Github-Repos nicht.
Siwei Shen 9
1
Warum sollten wir sie nicht benutzen? Der zweite sieht ziemlich nützlich aus.
Shamoon
1
Sie sollten Ihre Ping / Pong-Strategie umsetzen. Traue dem Open / Close-Ereignis nicht.
Siwei Shen 29