Javascript 使用 websockets 处理连接丢失

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26971026/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-22 23:35:15  来源:igfitidea点击:

Handling connection loss with websockets

javascriptfirefoxwebsocketfirefox-addon

提问by MLeFevre

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 neither the client or server intentionally initiated, i.e: Server loses power, ethernet cables pulled out etc... I need the client's to know whether connection has been lost within ~10seconds.

我最近设置了一个运行良好的本地 WebSocket 服务器,但是我在理解我应该如何处理客户端或服务器故意启动的突然丢失连接时遇到了一些麻烦,即:服务器断电,以太网电缆拔出等...我需要客户端知道连接是否在 ~10 秒内丢失。

Client side, connection is simply:

客户端,连接很简单:

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!');
};

I can manually trigger the connection disconnect which works fine,

我可以手动触发连接断开,效果很好,

websocket_conn.close();

But if I simply pulled the ethernet cable out the back of the computer, or disabled the connection, onclosedoesn't get called. I've read in another post that it would eventually get called when TCP detects loss of connectivity, but it's not in the timely manner that I need as the default for Firefox I believe is 10 minutes, and I don't really want to go around hundreds of computers about:configchanging this value. The only other suggestion I've read is to use a 'ping/pong' keep-alive polling style method which seems counterintuitive to the idea of websockets.

但是,如果我只是将以太网电缆从计算机背面拉出,或者禁用了连接,onclose则不会被调用。我在另一篇文章中读到它最终会在TCP 检测到连接丢失时被调用,但它不是我需要的及时方式,因为我认为 Firefox 的默认时间是 10 分钟,我真的不想去大约有数百台计算机about:config改变了这个值。我读过的唯一其他建议是使用“ping/pong”保持活动轮询样式方法,这似乎与 websockets 的想法相悖。

Is there an easier way to detect this kind of disconnect behaviour? Are the old posts i'm reading still up to date from a technical point, and the best method is still 'ping/pong' style?

有没有更简单的方法来检测这种断开连接的行为?从技术角度来看,我正在阅读的旧帖子是否仍然是最新的,最好的方法仍然是“乒乓”风格?

采纳答案by MLeFevre

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:configand searching for TCP.

与 websocket 服务器的连接发生在 Firefox 插件中,默认情况下,Firefox 的 TCP 设置有 10 分钟的超时时间。您可以查看about:config有关 TCP 和搜索 TCP 的其他详细信息。

Firefox addons can access these parameters

Firefox 插件可以访问这些参数

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 oncloseevent is triggered which displays an alert and also attempts to re-establish connection

所以现在,任何安装了插件的计算机都有 10 秒的 TCP 连接超时时间。如果连接丢失,onclose则触发事件,显示警报并尝试重新建立连接

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

回答by Sarath Ak

You have to add ping pong method

你必须添加乒乓方法

Create a code in server when receive __ping__send __pong__back

收到__ping__发送__pong__回时在服务器中创建代码

JavaScript code is give below

JavaScript 代码如下

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 --//
}

回答by Jade

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

我使用了 ping/pong 的想法,效果很好。这是我在 server.js 文件中的实现:

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:

这是我的 index.html 文件:

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

如果您想自己尝试,这里有一个指向 cloud 9 沙箱的链接:https: //ide.c9.io/design1online/web-sockets

回答by vtortola

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.

websocket 协议定义了 ping 和 pong 的控制帧。所以基本上,如果服务器发送一个 ping,浏览器会用一个 pong 回答,它也应该反过来工作。可能您使用的 WebSocket 服务器实现了它们,您可以定义浏览器必须响应或被视为死机的超时时间。这对于您在浏览器和服务器中的实现应该是透明的。

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

您可以使用它们来检测半开放连接:http: //blog.stephencleary.com/2009/05/detection-of-half-open-dropped.html

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

同样相关:WebSockets ping/pong,为什么不是 TCP keepalive?