Повторне підключення клієнта при перезавантаженні сервера в WebSocket


93

Я використовую веб-сокет, використовуючи PHP5 та браузер Chrome як клієнт. Я взяв код із веб-сайту http://code.google.com/p/phpwebsocket/ .

Я запускаю сервер, і клієнт також підключений. Я можу також поспілкуватися. Тепер, коли я перезапускаю сервер (вбиваючи його та запускаючи знову), клієнт отримує відключену інформацію, але автоматично не підключається до сервера, коли я надсилаю повідомлення.

Як цього досягти? Як, коли я отримую відключену інформацію, чи слід перевіряти її та відправляти в JavaScript для оновлення сторінки чи повторного підключення?

Відповіді:


143

Коли сервер перезавантажується, підключення до Web Socket переривається, тому oncloseспрацьовує подія JavaScript . Ось приклад, який намагається під’єднати кожні п’ять секунд.

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

3
Я сподівався, що є більш елегантний спосіб, без побудови нового об’єкта та визначення дій подій ...
ciembor

3
Через 5 хвилин браузер зависає. Я єдиний?
Марк

16
Слід додати "ws = null;" перед setTimeout (), щоб уникнути множення ws об’єктів та eventHandligs
Макс

8
Виправте мене, якщо я помиляюся, але цей код є небезпечним, оскільки певна кількість відключень призведе до переповнення стека. Це тому, що ви дзвоните startрекурсивно, ніколи не повертаючись.
Форівін

10
@Forivin Немає жодної проблеми зі stackoverflow тут. Оскільки в Javascript є лише один потік, який виконує наш код у будь-який момент, setTimeout () планує виконувати передану функцію в майбутньому, коли цей потік знову стане вільним. Після виклику тут setTimeout () потік повертається з функції (очищення стека), а потім переходить до обробки наступної події в черзі. Врешті-решт він потрапить до нашої анонімної функції, яка викликає старт і яка буде викликана як верхній кадр у стеку.
Stinky

39

Рішення, надане Ендрю, не працює абсолютно, оскільки у випадку втрати зв’язку сервер може надсилати кілька близьких подій.

У цьому випадку ви встановите кілька setTimout. Рішення, надане Ендрю, може працювати, лише якщо сервер готовий до п’яти секунд.

Потім, на основі переробленого рішення Andrew, я використав setInterval, прикріпивши ідентифікатор до об’єкта вікна (таким чином, він доступний "скрізь"):

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

ви все ще можете використовувати, setTimeoutякщо застосуєте до них ідею "глобальний ідентифікатор таймера";)
RozzA

3
"Рішення, дане Ендрю, може працювати, лише якщо сервер готовий до п'яти секунд." - Твердження не відповідає дійсності. Якщо сервер все ще буде недоступний через п'ять секунд, ваш клієнт не зможе відкрити з'єднання WebSocket, і oncloseподія буде знову запущена.
Sourav Ghosh

36

Повторне підключення WebSocket

GitHub розміщує невелику бібліотеку JavaScript, яка прикрашає API WebSocket, забезпечуючи з’єднання WebSocket, яке автоматично повторно підключатиметься, якщо з'єднання розірвано.

Зменшена бібліотека зі стисненням gzip - менше 600 байт.

Офіційне сховище доступне тут:

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

Затоплення сервера

Якщо до сервера під час перезавантаження підключена велика кількість клієнтів. Можливо, варто керувати термінами повторного підключення клієнтів, використовуючи експоненціальний алгоритм відстрочки.

Алгоритм працює так:

  1. Для k спроб генеруйте випадковий інтервал часу між 0 і 2 ^ k - 1,
  2. Якщо ви можете повторно підключитися, скиньте значення k до 1,
  3. Якщо повторне підключення не вдається, k збільшується на 1 і процес перезапускається на кроці 1,
  4. Щоб скоротити максимальний інтервал, коли досягнуто певної кількості спроб k, k перестає збільшуватися після кожної спроби.

Референція:

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

Повторне підключенняWebSocket не обробляє повторне підключення за допомогою цього алгоритму.


Чудова відповідь, особливо тому, що там згадується ризик великого навантаження сервера, коли сервер закриває з’єднання з веб-сокетом, і всі клієнти (яких може бути сотні чи тисячі) намагаються відновити зв’язок одночасно. Замість експоненціального зниження ви можете також рандомізувати затримку від 0 до 10 секунд. Це також розподілить навантаження на сервер.
Йохем Шуленклоппер

30

Я використовую цей зразок деякий час для чистого ванільного JavaScript, і він підтримує кілька випадків більше, ніж інші відповіді.

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


});

Це повториться, як тільки сервер закриє з’єднання, і він перевірить з’єднання, щоб переконатися, що воно також підключається кожні 5 секунд.

Отже, якщо сервер не працює, коли це запускається, або під час закритої події, підключення все одно повернеться, коли воно знову буде в мережі.

ПРИМІТКА: Використання цього сценарію не дозволить вам ніколи не припиняти спроби відкрити з'єднання ... але я думаю, що це те, що ви хочете?


7
Я б лише змінив: функцію check () {if (! Ws || ws.readyState === WebSocket.CLOSED) start (); }
dieresys

1
Такий підхід, а також описана тут методика збереження життя , здається, добре працює для мене.
Петро,

@Peter, не впевнений, що стан ws відкрито, потрібно (або потрібно) пінгувати, якщо я правильно, це вже в протоколі websocket. Це надмірне навантаження просто навантажило ваш сервер ...
comte

@comte деякі сервери ws відключають вас після `` періоду простою '', коли повідомлення від клієнта не надсилаються, і тому, щоб підтримувати з'єднання відкритим, пінг - це зла необхідність.
RozzA

2

Нижче наведені коди, які я використовував у своєму проекті, які працюють на 100%.

  1. Помістіть весь код веб-розетки всередину функції init.
  2. Усередині закритого зворотного дзвінка знову викличте init.
  3. Нарешті, викличте функцію init всередині функції готовності до документа.

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

2

Не можу коментувати, але таке:

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

Плюс https://www.npmjs.com/package/back - це вже досить добре :)


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.


0

Нарешті, я роблю автоматичне повторне підключення ws у vue + ts, аналогічно наступному:

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

0

Клієнтська подія закриття для WebSocket має властивість wasClean , що було мені корисно. Здається, для нього встановлено значення true у випадках, коли клієнтський комп'ютер переходить у сплячий режим тощо, або коли сервер несподівано зупиняється і т. Д. Він встановлюється як false, якщо ви вручну закриваєте сокет, і в цьому випадку ви не хочете відкривати знову розетку автоматично. Нижче коду з проекту Angular 7. У мене є цей код у службі, тому він може використовуватися з будь-якого компонента.

    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);   
        } 
    }
    .....
    .....
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.