Umgang mit Verbindungsverlusten mit WebSockets

Lesezeit: 10 Minuten

Benutzeravatar von SubjectCurio
BetreffCurio

Ich habe kürzlich einen lokalen WebSocket-Server eingerichtet, der einwandfrei funktioniert. Ich habe jedoch einige Probleme zu verstehen, wie ich mit einem plötzlichen Verbindungsverlust umgehen soll, der weder vom Client noch vom Server absichtlich ausgelöst wurde, z. B.: Server verliert Strom, Ethernet-Kabel herausgezogen usw. Ich möchte, dass der Client innerhalb von ca. 10 Sekunden weiß, ob die Verbindung unterbrochen wurde.

Clientseitig ist die Verbindung einfach:

var websocket_conn = new WebSocket('ws://192.168.0.5:3000');

websocket_conn.onopen = function(e) {
    console.log('Connected!');
};

websocket_conn.onclose = function(e) {
    console.log('Disconnected!');
};

Ich kann die Verbindungstrennung manuell auslösen, was einwandfrei funktioniert.

websocket_conn.close();

Aber wenn ich einfach das Ethernet-Kabel an der Rückseite des Computers herausziehe oder die Verbindung deaktiviere, onclose wird nicht angerufen. Ich habe in einem anderen Beitrag gelesen, dass es irgendwann aufgerufen wird, wenn TCP einen Verbindungsverlust feststellt, aber nicht in der Zeit, die ich brauche, da der Standardwert für Firefox meiner Meinung nach 10 Minuten beträgt und ich das eigentlich nicht tun möchte rund Hunderte von Computern about:config diesen Wert ändern. Der einzige andere Vorschlag, den ich gelesen habe, ist die Verwendung einer „Ping/Pong“-Keep-Alive-Abfragemethode, die der Idee von Websockets zu widersprechen scheint.

Gibt es eine einfachere Möglichkeit, diese Art von Verbindungsabbruchverhalten zu erkennen? Sind die alten Beiträge, die ich lese, technisch noch aktuell und die beste Methode ist immer noch der „Ping/Pong“-Stil?

  • Wenn Sie sich nicht mit Keep-Alive-Tokens, der Planung von Wiederverbindungsversuchen usw. befassen möchten, sollten Sie eine bewährte Bibliothek verwenden. socket.io kommt in den Sinn.

    – Denys Séguret

    17. November 2014 um 10:55

Sie müssen die Ping-Pong-Methode hinzufügen

Erstellen Sie beim Empfang einen Code auf dem Server __Klingeln__ schicken __pong__ zurück

Der JavaScript-Code ist unten angegeben

function ping() {
        ws.send('__ping__');
        tm = setTimeout(function () {

           /// ---connection closed ///


    }, 5000);
}

function pong() {
    clearTimeout(tm);
}
websocket_conn.onopen = function () {
    setInterval(ping, 30000);
}
websocket_conn.onmessage = function (evt) {
    var msg = evt.data;
    if (msg == '__pong__') {
        pong();
        return;
    }
    //////-- other operation --//
}

  • Dies war für mich in Kombination mit dieser Websocket-Implementierung zum erneuten Verbinden hier wirklich nützlich: github.com/joewalnes/reconnecting-websocket mit einer Einschränkung: Ich musste den Intervall-Timer auch einer Variablen zuweisen und ihn innerhalb der Ping-Timeout-Funktion löschen.

    – Löwe

    31. Juli 2019 um 12:40 Uhr


  • Was nützt Websocket, wenn Sie damit Umfragen durchführen 🙂

    – Steve Moretz

    18. Oktober 2021 um 7:51 Uhr

  • @stevemoretz Das frage ich mich tatsächlich

    – manudicri

    20. Januar 2022 um 21:58

  • Websockets scheinen eine wirklich dünne Schicht zu TCP/IP zu sein. Wenn Sie also eine zusätzliche Funktion über die aktuelle Implementierung von TCP/IP hinaus implementieren müssen, müssen Sie diese selbst hinzufügen. Daher gab der Antwortende an, dass Sie Ping/Ping selbst hinzufügen müssen. Es wäre jedoch schön, wenn WebSockets dies eingebaut hätten, damit es konfigurierbar und allgemeiner wäre, da es anscheinend ein häufiges Ärgernis ist.

    – LeanMan

    27. Januar 2022 um 9:15 Uhr

Benutzeravatar von SubjectCurio
BetreffCurio

Dies war die Lösung, die ich letztendlich gefunden habe und die vorerst gut zu funktionieren scheint. Sie ist völlig spezifisch für die Einrichtung meines Projekts und basiert auf Kriterien, die ursprünglich nicht in meiner Frage erwähnt wurden, aber sie könnte für jemand anderen nützlich sein wenn sie zufällig das Gleiche tun.

Die Verbindung zum Websocket-Server erfolgt innerhalb eines Firefox-Add-ons und standardmäßig hat das TCP-Setup von Firefox eine Zeitüberschreitung von 10 Minuten. Weitere Details können Sie mit einsehen about:config und Suche nach TCP.

Firefox-Add-ons können auf diese Parameter zugreifen

var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService);

und ändern Sie diese Parameter auch, indem Sie den Zweig und die Präferenz zusammen mit dem neuen Wert angeben

prefs.getBranch("network.http.tcp_keepalive.").setIntPref('long_lived_idle_time', 10);

Jetzt hat jeder Computer, auf dem das Add-on installiert ist, eine Zeitüberschreitung von 10 Sekunden für TCP-Verbindungen. Wenn die Verbindung unterbrochen wird, wird die onclose Es wird ein Ereignis ausgelöst, das eine Warnung anzeigt und außerdem versucht, die Verbindung wiederherzustellen

websocket_conn.onclose = function (e) {
    document.getElementById('websocket_no_connection').style.display = 'block';
    setTimeout(my_extension.setup_websockets, 10000);
}; 

  • Da es kaum eine passendere Antwort geben kann, wäre es hier wahrscheinlich die beste Vorgehensweise, die Ihre zu akzeptieren.

    – Denys Séguret

    20. November 2014 um 12:20 Uhr

Jades Benutzeravatar
Jade

Ich habe die Ping-/Pong-Idee verwendet und sie funktioniert gut. Hier ist meine Implementierung in meiner server.js-Datei:

var SOCKET_CONNECTING = 0;
var SOCKET_OPEN = 1;
var SOCKET_CLOSING = 2;
var SOCKET_CLOSED = 3;

var WebSocketServer = require('ws').Server
wss = new WebSocketServer({ port: 8081 });

//Send message to all the users
wss.broadcast = function broadcast(data,sentBy)
{
  for (var i in this.clients)
  {
    this.clients[i].send(data);
  }
};

var userList = [];
var keepAlive = null;
var keepAliveInterval = 5000; //5 seconds

//JSON string parser
function isJson(str)
{
 try {
    JSON.parse(str);
  }
  catch (e) {
    return false;
  }
  return true;
}

//WebSocket connection open handler
wss.on('connection', function connection(ws) {
  
  function ping(client) {
    if (ws.readyState === SOCKET_OPEN) {
      ws.send('__ping__');
    } else {
      console.log('Server - connection has been closed for client ' + client);
      removeUser(client);
    }
  }
  
  function removeUser(client) {
    
    console.log('Server - removing user: ' + client)
    
    var found = false;
    for (var i = 0; i < userList.length; i++) {
      if (userList[i].name === client) {
        userList.splice(i, 1);
        found = true;
      }
    }
    
    //send out the updated users list
    if (found) {
      wss.broadcast(JSON.stringify({userList: userList}));
    };
    
    return found;
  }
  
  function pong(client) {
    console.log('Server - ' + client + ' is still active');
    clearTimeout(keepAlive);
    setTimeout(function () {
      ping(client);
    }, keepAliveInterval);
  }

  //WebSocket message receive handler
  ws.on('message', function incoming(message) {
    if (isJson(message)) {
      var obj = JSON.parse(message);
      
      //client is responding to keepAlive
      if (obj.keepAlive !== undefined) {
        pong(obj.keepAlive.toLowerCase());
      }
      
      if (obj.action === 'join') {
        console.log('Server - joining', obj);
        
        //start pinging to keep alive
        ping(obj.name.toLocaleLowerCase());
        
        if (userList.filter(function(e) { return e.name == obj.name.toLowerCase(); }).length <= 0) {
          userList.push({name: obj.name.toLowerCase()});
        }
        
        wss.broadcast(JSON.stringify({userList: userList}));
        console.log('Server - broadcasting user list', userList);
      }
    }
    
    console.log('Server - received: %s', message.toString());
    return false;
  });
});

Hier ist meine index.html-Datei:

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
        <title>Socket Test</title>
    </head>
    <body>
        <div id="loading" style="display: none">
            <p align="center">
                LOADING...
            </p>
        </div>
        <div id="login">
            <p align="center">
                <label for="name">Enter Your Name:</label>
                <input type="text" id="name" />
                <select id="role">
                    <option value="0">Attendee</option>
                    <option value="1">Presenter</option>
                </select>
                <button type="submit" onClick="login(document.getElementById('name').value, document.getElementById('role').value)">
                    Join
                </button>
            </p>
        </div>
        <div id="presentation" style="display: none">
            <div class="slides">
                <section>Slide 1</section>
                <section>Slide 2</section>
            </div>
            <div id="online" style="font-size: 12px; width: 200px">
                <strong>Users Online</strong>
                <div id="userList">
                </div>
            </div>
        </div>
        <script>
            function isJson(str) {
                try {
                   JSON.parse(str);
                }
                catch (e) {
                   return false;
                }
                return true;
            }

            var ws;
            var isChangedByMe = true;
            var name = document.getElementById('name').value;
            var role = document.getElementById('role').value;

            function init()
            {
                loading = true;
                ws = new WebSocket('wss://web-sockets-design1online.c9users.io:8081');

                //Connection open event handler
                ws.onopen = function(evt)
                {
                    ws.send(JSON.stringify({action: 'connect', name: name, role: role}));
                }

                ws.onerror = function (msg) {
                    alert('socket error:' + msg.toString());
                }

                //if their socket closes unexpectedly, re-establish the connection
                ws.onclose = function() {
                    init();
                }
                
                //Event Handler to receive messages from server
                ws.onmessage = function(message)
                {
                    console.log('Client - received socket message: '+ message.data.toString());
                    document.getElementById('loading').style.display = 'none';

                    if (message.data) {

                        obj = message.data;
                    
                        if (obj.userList) {
                        
                            //remove the current users in the list
                            userListElement = document.getElementById('userList');
                            
                            while (userListElement.hasChildNodes()) {
                                userListElement.removeChild(userListElement.lastChild);
                            }

                            //add on the new users to the list
                            for (var i = 0; i < obj.userList.length; i++) {
                            
                                var span = document.createElement('span');
                                span.className="user";
                                span.style.display = 'block';
                                span.innerHTML = obj.userList[i].name;
                                userListElement.appendChild(span);
                            }
                        }
                    }

                    if (message.data === '__ping__') {
                        ws.send(JSON.stringify({keepAlive: name}));
                    }

                    return false;
                }
            }

            function login(userName, userRole) {

                if (!userName) {
                    alert('You must enter a name.');
                    return false;
                } 

                //set the global variables
                name = userName;
                role = userRole;

                document.getElementById('loading').style.display = 'block';
                document.getElementById('presentation').style.display = 'none';
                document.getElementById('login').style.display = 'none';
                init();
            }
        </script>
    </body>
</html>

Hier ist ein Link zur Cloud 9-Sandbox, wenn Sie es selbst ausprobieren möchten: https://ide.c9.io/design1online/web-sockets

  • Ich glaube, Ihnen fehlen eine Handvoll Variablen und Codezeilen. obj.action === ‘join’ wo kommt das überhaupt her? Und ich denke, Ihre Variable fehlt, um Ihren setTimeout zu löschen.

    – Brad Vanderbush

    30. Mai 2020 um 15:00 Uhr

  • Der obj.action ===-Beitritt erfolgt durch Klicken auf die Schaltfläche „Beitreten“. Der setTimeout wird im keepAlive referenziert.

    – Jade

    16. Juni 2020 um 17:16 Uhr

  • Warum wss.broadcast zweimal definieren?

    – Temirbek

    9. November 2021 um 9:25 Uhr

  • Das ist nur ein Tippfehler, behoben, Ty

    – Jade

    22. November 2021 um 18:22 Uhr


Benutzeravatar von vtortola
vtortola

Das Websocket-Protokoll definiert Kontrollrahmen für Tischtennis und Tischtennis. Wenn also der Server einen Ping sendet, antwortet der Browser mit einem Pong, und umgekehrt sollte es auch funktionieren. Wahrscheinlich implementiert der von Ihnen verwendete WebSocket-Server sie, und Sie können ein Timeout definieren, in dem der Browser antworten muss, sonst gilt er als tot. Dies sollte für Ihre Implementierung sowohl im Browser als auch auf dem Server transparent sein.

Mit ihnen können Sie halboffene Verbindungen erkennen: http://blog.stephencleary.com/2009/05/detection-of-half-open-dropped.html

Auch relevant: WebSockets Ping/Pong, warum nicht TCP Keepalive?

Okay, ich komme zu spät zur Party, hoffe aber, dass ich hier etwas hinzufügen kann. Meine TypeScript-Implementierung in einem Angular App zu handhaben WebSocket Verbindung unterbrochen. Dies wird nicht verwendet PING PONG Strategie, um zu vermeiden, dass der Server ständig ausgelastet ist. Dies beginnt erst dann mit dem Versuch, eine Verbindung herzustellen, wenn die Verbindung verloren geht, und versucht es alle 5 Sekunden so lange, bis die Verbindung erfolgreich hergestellt wurde. Auf geht’s:

export class WebSocketClientComponent implements OnInit {

   webSocket?: WebSocket;
   selfClosing = false;
   reconnectTimeout: any;

   ngOnInit(): void {
      this.establishWebSocketConnection();
   }

   establishWebSocketConnection() {

      this.webSocket = new WebSocket('YourServerURlHere');
 
      this.webSocket.onopen = (ev: any) => {
  
         if (this.reconnectTimeout) {
            clearTimeout(this.reconnectTimeout);
         }
      }

      this.webSocket.onclose = (ev: CloseEvent) => {
  
         if (this.selfClosing === false) {
            this.startAttemptingToEstablishConnection();
         }
      }
   }

   private startAttemptingToEstablishConnection() {
      this.reconnectTimeout = setTimeout(() => this.establishWebSocketConnection(), 5000);
   }
}

Und das ist es. Wenn Sie die WebSocket-Verbindung zu einem bestimmten Zeitpunkt schließen möchten, legen Sie fest selfClosing = true. Dies würde den Versuch unterbinden, die Verbindung erneut herzustellen. Hoffe das hilft. Ich bin sicher, dass derselbe Code nativ verwendet werden kann JS sowie.

  • Das ist sehr nützlich für die Wiederherstellung der Verbindung, aber ich denke, das Problem des OP bestand darin, dass es zu lange gedauert hat, bis es erkannt hat, dass die Verbindung verloren gegangen ist.

    – Andrew W. Phillips

    1. September 2022 um 7:09

  • Das ist sehr nützlich für die Wiederherstellung der Verbindung, aber ich denke, das Problem des OP bestand darin, dass es zu lange gedauert hat, bis es erkannt hat, dass die Verbindung verloren gegangen ist.

    – Andrew W. Phillips

    1. September 2022 um 7:09

1451920cookie-checkUmgang mit Verbindungsverlusten mit WebSockets

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy