在 WebSocket 中重新启动服务器时重新连接客户端

我使用网络套接字使用 PHP5和 Chrome 浏览器作为客户端。 我从 http://code.google.com/p/phpwebsocket/站点获取了代码。

我运行服务器,客户端也连接。我也可以聊天。 现在,当我重新启动服务器(通过关闭它并重新启动它)时, 客户端获取断开连接的信息,但在发送消息时不会自动重新连接到服务器。

如何做到这一点?比如当我得到断开连接的信息时,我应该检查它并将其发送到 JavaScript 以刷新页面还是重新连接?

100610 次浏览

When the server reboots, the Web Socket connection is closed, so the JavaScript onclose event is triggered. Here's an example that tries to reconnect every five seconds.

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

The solution given by Andrew isn't perfectly working because, in case of lost connection, the server might send several close events.

In that case, you'll set several setTimout's. The solution given by Andrew may only work if the server is ready before five seconds.

Then, based on Andrew solution, reworked, I've made use of setInterval attaching the ID to the window object (that way it is available "everywhere"):

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

I have been using this patten for a while for pure vanilla JavaScript, and it supports a few more cases than the other answers.

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);




});

This will retry as soon as the server closes the connection, and it will check the connection to make sure it's up every 5 seconds also.

So if the server is not up when this runs or at the time of the onclose event the connection will still come back once it's back online.

NOTE: Using this script will not allow you to ever stop trying to open a connection... but I think that's what you want?

Below are the codes i have used in my project which working 100%.

  1. Put all the websocket code inside the init function.
  2. Inside the onclose callback call the init again.
  3. Finally call the init function inside the document ready function.

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();   };
}

ReconnectingWebSocket

There is a small JavaScript library that decorates the WebSocket API to provide a WebSocket connection that will automatically reconnect if the connection is dropped.

Minified library with gzip compression is less than 600 bytes.

The repository is available here:

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

There is also a TypeScript Library. Just include it and replace new WebSocket with new ReconnectingWebSocket.

The repository is available here:

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

Server flood

If a high number of clients are connected to the server when it reboots. It may be worthwhile to manage the reconnect timings of the clients by using an Exponential Backoff algorithm.

The algorithm works like this:

  1. For k attempts, generate a random interval of time between 0 and 2^k - 1,
  2. If you are able to reconnect, reset k to 1,
  3. If reconnection fails, k increases by 1 and the process restarts at step 1,
  4. To truncate the max interval, when a certain number of attempts k has been reached, k stops increasing after each attempt.

Référence:

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

ReconnectingWebSocket does not handle reconnections by using this algorithm.

Can't comment, but the following:

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 is already good enough :)

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.

Finally, I make ws auto reconnect in vue+ts, similar as following:

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

The client side close event for WebSocket has a wasClean property, which was useful to me. Seems like it is set to true in cases where the client computer goes into sleep mode etc. or when the server is unexpectedly stopped etc. It is set to false if you manually close the socket, in which case you don't want to open the socket automatically again. Below code from an Angular 7 project. I have this code in a service, so it is usable from any component.

    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);
}
}
.....
.....