Erneutes Verbinden des Clients beim Neustart des Servers in WebSocket

93

Ich verwende Web Socket mit PHP5 und dem Chrome-Browser als Client. Ich habe den Code von der Website http://code.google.com/p/phpwebsocket/ übernommen .

Ich starte den Server und der Client ist auch verbunden. Ich kann auch chatten. Wenn ich den Server neu starte (indem ich ihn beende und erneut starte), erhält der Client die getrennten Informationen, stellt jedoch beim Senden der Nachricht automatisch keine Verbindung zum Server wieder her.

Wie erreicht man das? Wenn ich die nicht verbundenen Informationen erhalte, sollte ich sie überprüfen und an JavaScript senden, um die Seite zu aktualisieren oder die Verbindung wiederherzustellen?

siddhusingh
quelle

Antworten:

143

Beim Neustart des Servers wird die Web Socket-Verbindung geschlossen, sodass das JavaScript- oncloseEreignis ausgelöst wird. Hier ist ein Beispiel, das alle fünf Sekunden versucht, die Verbindung wiederherzustellen.

function start(websocketServerLocation){
    ws = new WebSocket(websocketServerLocation);
    ws.onmessage = function(evt) { alert('message received'); };
    ws.onclose = function(){
        // Try to reconnect in 5 seconds
        setTimeout(function(){start(websocketServerLocation)}, 5000);
    };
}
Andrew
quelle
3
Ich hoffte, dass es einen eleganteren Weg gibt, ohne ein neues Objekt zu konstruieren und Ereignisaktionen zu definieren ...
ciembor
3
Nach 5 Minuten friert der Browser ein. Bin ich der Einzige?
Marc
16
Sie sollten "ws = null;" hinzufügen. vor setTimeout (), um zu vermeiden, ws Objekte und eventHandligs zu multiplizieren
Max
8
Korrigieren Sie mich, wenn ich falsch liege, aber dieser Code ist gefährlich, da eine bestimmte Anzahl von Verbindungsabbrüchen einen Stapelüberlauf verursacht. Das liegt daran, dass Sie startrekursiv anrufen , ohne jemals zurückzukehren.
Forivin
10
@Forivin Hier gibt es kein Problem mit dem Stapelüberlauf. Da es in Javascript nur einen einzelnen Thread gibt, der unseren Code zu einem bestimmten Zeitpunkt ausführt, plant setTimeout (), dass die übergebene Funktion in Zukunft ausgeführt wird, wenn dieser einzelne Thread wieder frei ist. Nachdem setTimeout () hier aufgerufen wurde, kehrt der Thread von der Funktion zurück (Löschen des Stapels) und verarbeitet dann das nächste Ereignis in der Warteschlange. Es wird schließlich zu unserer anonymen Funktion gelangen, die Aufrufe starten und die als oberster Frame im Stapel aufgerufen werden.
Stinky
39

Die von Andrew angegebene Lösung funktioniert nicht einwandfrei, da der Server im Falle eines Verbindungsverlusts möglicherweise mehrere Abschlussereignisse sendet.

In diesem Fall legen Sie mehrere setTimout-Werte fest. Die von Andrew angegebene Lösung funktioniert möglicherweise nur, wenn der Server vor fünf Sekunden bereit ist.

Dann habe ich basierend auf der überarbeiteten Andrew-Lösung setInterval verwendet, um die ID an das Fensterobjekt anzuhängen (auf diese Weise ist sie "überall" verfügbar):

var timerID=0;

var socket;

/* Initiate what has to be done */

socket.onopen=function(event){
 /* As what was before */
 if(window.timerID){ /* a setInterval has been fired */
   window.clearInterval(window.timerID);
   window.timerID=0;
 }
 /* ... */
}

socket.onclose=function(event){
  /* ... */
 if(!window.timerID){ /* Avoid firing a new setInterval, after one has been done */
  window.timerID=setInterval(function(){start(websocketServerLocation)}, 5000);
 }
 /* That way, setInterval will be fired only once after losing connection */
 /* ... */
}
user2909737
quelle
Sie können immer noch verwenden, setTimeoutwenn Sie die Idee "Global Timer ID" auf sie
anwenden
3
"Die von Andrew angegebene Lösung funktioniert möglicherweise nur, wenn der Server vor fünf Sekunden bereit ist." - Die Aussage ist nicht wahr. Wenn der Server nach fünf Sekunden immer noch nicht verfügbar ist, kann Ihr Client keine WebSocket-Verbindung herstellen und das oncloseEreignis wird erneut ausgelöst.
Sourav Ghosh
36

WebSocket erneut verbinden

GitHub hostet eine kleine JavaScript-Bibliothek, die die WebSocket-API dekoriert, um eine WebSocket-Verbindung bereitzustellen, die sich automatisch wieder verbindet, wenn die Verbindung getrennt wird.

Die minimierte Bibliothek mit gzip-Komprimierung beträgt weniger als 600 Byte.

Das offizielle Repository finden Sie hier:

https://github.com/joewalnes/reconnecting-websocket

Serverflut

Wenn beim Neustart eine große Anzahl von Clients mit dem Server verbunden ist. Es kann sich lohnen, die Wiederverbindungszeiten der Clients mithilfe eines Exponential Backoff-Algorithmus zu verwalten.

Der Algorithmus funktioniert folgendermaßen:

  1. Generieren Sie für k Versuche ein zufälliges Zeitintervall zwischen 0 und 2 ^ k - 1,
  2. Wenn Sie die Verbindung wieder herstellen können, setzen Sie k auf 1 zurück.
  3. Wenn die Wiederverbindung fehlschlägt, erhöht sich k um 1 und der Prozess wird in Schritt 1 neu gestartet.
  4. Um das maximale Intervall abzuschneiden, hört k nach jedem Versuch auf zuzunehmen, wenn eine bestimmte Anzahl von Versuchen k erreicht wurde.

Referenz:

http://blog.johnryding.com/post/78544969349/how-to-reconnect-web-sockets-in-a-realtime-web-app

ReconnectingWebSocket verarbeitet keine erneuten Verbindungen mithilfe dieses Algorithmus.

Joël Esponde
quelle
Gute Antwort, insbesondere, weil das Risiko einer hohen Serverlast erwähnt wird, sobald der Server die Web-Socket-Verbindungen schließt und alle Clients (die Hunderte oder Tausende sein können) gleichzeitig versuchen, die Verbindung wiederherzustellen. Anstelle eines exponentiellen Backoffs können Sie die Verzögerung auch zufällig zwischen 0 und 10 Sekunden einstellen. Dadurch wird auch die Belastung des Servers verteilt.
Jochem Schulenklopper
29

Ich benutze dieses Muster seit einiger Zeit für reines Vanille-JavaScript und es unterstützt ein paar Fälle mehr als die anderen Antworten.

document.addEventListener("DOMContentLoaded", function() {

  'use strict';

  var ws = null;

  function start(){

    ws = new WebSocket("ws://localhost/");
    ws.onopen = function(){
      console.log('connected!');
    };
    ws.onmessage = function(e){
      console.log(e.data);
    };
    ws.onclose = function(){
      console.log('closed!');
      //reconnect now
      check();
    };

  }

  function check(){
    if(!ws || ws.readyState == 3) start();
  }

  start();

  setInterval(check, 5000);


});

Dies wird wiederholt, sobald der Server die Verbindung schließt, und es wird die Verbindung überprüft, um sicherzustellen, dass sie auch alle 5 Sekunden aktiv ist.

Wenn der Server zum Zeitpunkt der Ausführung oder zum Zeitpunkt des Onclose-Ereignisses nicht aktiv ist, wird die Verbindung immer noch wiederhergestellt, sobald sie wieder online ist.

HINWEIS: Wenn Sie dieses Skript verwenden, können Sie nie aufhören, eine Verbindung herzustellen ... aber ich denke, das ist es, was Sie wollen?

komplistisch
quelle
7
Ich würde nur ändern: function check () {if (! Ws || ws.readyState === WebSocket.CLOSED) start (); }
Dieresys
1
Dieser Ansatz, sowie eine Keep-Alive - Technik beschrieben hier , scheint funktioniert gut für mich.
Peter
@Peter, nicht sicher, ob der ws-Status geöffnet ist, müssen (oder sollten) pingen, wenn ich richtig bin, ist es bereits im Websocket-Protokoll. Dieser Overkill hatte gerade Last auf Ihrem Server ...
Comte
@comte Einige WS-Server trennen Sie nach einer Leerlaufzeit, in der keine Nachrichten vom Client gesendet wurden. Um die Verbindung offen zu halten, ist der Ping eine böse Notwendigkeit.
RozzA
2

Unten sind die Codes aufgeführt, die ich in meinem Projekt verwendet habe und die zu 100% funktionieren.

  1. Fügen Sie den gesamten Websocket-Code in die Init-Funktion ein.
  2. Rufen Sie innerhalb des Onclose-Rückrufs den Init erneut auf.
  3. Rufen Sie abschließend die Init-Funktion in der Document Ready-Funktion auf.

var name = sessionStorage.getItem ('name');

wsUri =  "ws://localhost:8080";   
var websocket;
$(function() {  
    init();  
    $("#chat_text_box").on("keypress", function(e) {         
        if (e.keyCode == 13) {   //For Enter Button    
            e.preventDefault();
            var mymessage = $('#chat_text_box').val();               
            if(mymessage){
                var msg = {  type: 'chat_text',  data : {  name:name,  msg:mymessage }  };                
                console.log(msg);
                websocket.send(JSON.stringify(msg));
                $('#chat_text_box').val('');
            }               
            return false;                       
        }        
    });      
});     
function init() { 
    websocket = new WebSocket(wsUri);      
    websocket.onopen = function(ev) { /*connection is open */    } 
    websocket.onmessage = function(ev) {        
        var data = JSON.parse(ev.data); //PHP sends Json data        
        var type = data.type;//alert(JSON.stringify(data));
        switch(type) {
            case "chat_text":
                var text = "<div><span class='user'>"+data.data.sender_name+" : </span><span class='msg'>"+data.data.msg+"</span></div>";
                $('#chat-messages').append(text);
                break;            
            default:
                break;

        }        

    };     
    websocket.onerror   = function(ev){}; 
    websocket.onclose = function(ev) {   init();   };  
}
Pradeepta
quelle
2

Kann nicht kommentieren, aber die folgenden:

var socket;

const socketMessageListener = (event) => {
  console.log(event.data);
};

const socketOpenListener = (event) => {
  console.log('Connected');
  socket.send('hello');
};

const socketCloseListener = (event) => {
  if (socket) {
    console.error('Disconnected.');
  }
  socket = new WebSocket('ws://localhost:8080');
  socket.addEventListener('open', socketOpenListener);
  socket.addEventListener('message', socketMessageListener);
  socket.addEventListener('close', socketCloseListener);
};

socketCloseListener();

// for testing
setTimeout(()=>{
  socket.close();
},5000);

Plus https://www.npmjs.com/package/back ist schon gut genug :)

xemasiv
quelle
1

function wsConnection(url){
    var ws = new WebSocket(url);
    var s = (l)=>console.log(l);
	ws.onopen = m=>s(" CONNECTED")
    ws.onmessage = m=>s(" RECEIVED: "+JSON.parse(m.data))
    ws.onerror = e=>s(" ERROR")
    ws.onclose = e=>{
        s(" CONNECTION CLOSED");
        setTimeout((function() {
            var ws2 = new WebSocket(ws.url);
			ws2.onopen=ws.onopen;
            ws2.onmessage = ws.onmessage;
            ws2.onclose = ws.onclose;
            ws2.onerror = ws.onerror;
            ws = ws2
        }
        ).bind(this), 5000)
    }
    var f = m=>ws.send(JSON.stringify(m)) || "Sent: "+m;
    f.ping = ()=>ws.send(JSON.stringify("ping"));
    f.close = ()=>ws.close();
    return f
}

c=new wsConnection('wss://echo.websocket.org');
setTimeout(()=>c("Hello world...orld...orld..orld...d"),5000);
setTimeout(()=>c.close(),10000);
setTimeout(()=>c("I am still alive!"),20000);
<pre>
This code will create a websocket which will 
reconnect automatically after 5 seconds from disconnection.

An automatic disconnection is simulated after 10 seconds.

Zibri
quelle
0

Schließlich mache ich ws automatische Wiederverbindung in vue + ts, ähnlich wie folgt:

private async mounted() {
    // Connect to WebSocket
    const sn = "sb1234567890";
    const host =
        window.location.protocol == "https:"
            ? "wss://www.xxx.net"
            : process.env.DEV_TYPE === "fullstack"
            ? "ws://10.0.0.14:8528"
            : "ws://www.xxx.net:8528";
    const wsUri = host + "/feed-home/" + sn;
    await this.startWs(wsUri, sn);
    // !!!Deprecated: failed to reconnect
    // let ws = new WebSocket();
    // console.log(ws);
    // ws.onopen = async function(event) {
    //     console.log(event, "openEvent");
    //     clearInterval(that.timer);
    // };
    // ws.onclose = async function(event) {
    //     console.log(event, "close");
    //     that.timer = setInterval(() => {
    //         console.log("Heart Beat");
    //         ws.send("HeartBeat");
    //         // ws = new WebSocket("ws://10.0.0.14:8528/feed-home/" + sn);
    //         console.log(ws);
    //     }, 60000);
    // };
    // ws.onmessage = async function(event) {
    //     console.log(event, "ws");
    //     alert("get it!");
    //     await alert("please update!");
    //     await that.getHome(sn);
    // };
}
private wsReconnected: boolean = false; // check whether WebSocket is reconnected
private async startWs(uri: string, sn: string) {
    let that = this;
    let ws = new WebSocket(uri);
    ws.onopen = async () => {
        if (that.wsReconnected) {
            await that.getHome(sn); // Refresh api data after reconnected
        }
        ws.send("Current client version: " + window.version);
    };
    ws.onmessage = async evt => {
        await that.getHome(sn);
        that.$message({
            type: "success",
            message: evt.data,
            showClose: true,
            center: true,
            duration: 20 * 1000
        });
    };
    ws.onclose = async () => {
        that.wsReconnected = true;
        await that.startWs(uri, sn);
        const sleep = (seconds: number) => {
            return new Promise(resolve =>
                setTimeout(resolve, seconds * 1000)
            );
        };
        await sleep(10); // Try to reconnect in 10 seconds
        // !!!Deprecated: Use timer will cause multiply ws connections
        // if (!that.wsTimer) {
        //     // Try to reconnect in 10 seconds
        //     that.wsTimer = setInterval(async () => {
        //         console.log("reconnecting websocket...");
        //         await that.startWs(uri, sn);
        //     }, 10 * 1000);
        // }
    };
}
Waket Zheng
quelle
0

Das clientseitige Abschlussereignis für WebSocket verfügt über eine wasClean- Eigenschaft, die für mich hilfreich war. Scheint so, als wäre es auf true gesetzt, wenn der Client-Computer in den Ruhemodus wechselt usw. oder wenn der Server unerwartet gestoppt wird usw. Es wird auf false gesetzt, wenn Sie den Socket manuell schließen. In diesem Fall möchten Sie nicht öffnen die Steckdose automatisch wieder. Unten Code aus einem Angular 7-Projekt. Ich habe diesen Code in einem Dienst, so dass er von jeder Komponente verwendet werden kann.

    notifySocketClose(event) { 

        if (!event.wasClean) { 
            setTimeout(() => {
                this.setupSocket()
            }, 1000);       
        }
    }

    setupSocket() { // my function to handle opening of socket, event binding etc.
    .....
    .....

            this.websocketConnection = this.websocketConnection ? this.websocketConnection : new WebSocket(socketUrl);
            this.websocketConnection.onclose = this.notifySocketClose.bind(this);   
        } 
    }
    .....
    .....
leoncc
quelle