Handling connection loss with websockets

前端 未结 4 1809
清酒与你
清酒与你 2020-12-07 10:32

I\'ve recently set-up a local WebSocket server which works fine, however I\'m having a few troubles understanding how I should handle a sudden loss of connection which neith

相关标签:
4条回答
  • 2020-12-07 10:33

    You have to add ping pong method

    Create a code in server when receive __ping__ send __pong__ back

    JavaScript code is give below

    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 --//
    }
    
    0 讨论(0)
  • 2020-12-07 10:35

    I used the ping/pong idea and it works nicely. Here's my implementation in my server.js file:

    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 });
    
    //Broadcast method to send message to all the users
    wss.broadcast = function broadcast(data,sentBy)
    {
      for (var i in this.clients)
      {
        if(this.clients[i] != sentBy)
        {
          this.clients[i].send(data);
        }
      }
    };
    
    //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;
      });
    });
    

    Here's my index.html file:

    <!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>
    

    Here's a link to the cloud 9 sandbox if you want to try it out yourself: https://ide.c9.io/design1online/web-sockets

    0 讨论(0)
  • 2020-12-07 10:48

    The websocket protocol defines control frames for ping and pong. So basically, if the server sends a ping, the browser will answer with a pong, and it should work also the other way around. Probably the WebSocket server you use implements them, and you can define a timeout in which the browser must responds or be considered dead. This should be transparent for your implementation in both browser and server.

    You can use them to detect half open connections: http://blog.stephencleary.com/2009/05/detection-of-half-open-dropped.html

    Also relevant: WebSockets ping/pong, why not TCP keepalive?

    0 讨论(0)
  • 2020-12-07 10:49

    This was the solution I ended up doing which seems to work fine for the time being, it's entirely specific to my project's setup & relies on criteria being in place that wasn't originally mentioned in my question, but it might be useful for someone else if they happen to be doing the same thing.

    The connection to the websocket server occurs within a Firefox addon, and by default Firefox's TCP setup has a 10 minute timeout. You can see additional details with about:config and searching for TCP.

    Firefox addons can access these parameters

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

    and also change these parameters by specifying the branch & preference along with the new value

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

    So now, any computer with the addon installed have a 10 second timeout for TCP connections. If the connection is lost, the onclose event is triggered which displays an alert and also attempts to re-establish connection

    websocket_conn.onclose = function (e) {
        document.getElementById('websocket_no_connection').style.display = 'block';
        setTimeout(my_extension.setup_websockets, 10000);
    }; 
    
    0 讨论(0)
提交回复
热议问题