Повторное подключение клиента при перезагрузке сервера в WebSocket


94

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

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

Как этого добиться? Например, когда я получаю информацию об отключении, следует ли мне ее проверить и отправить в JavaScript, чтобы обновить страницу или повторно подключиться?

Ответы:


143

Когда сервер перезагружается, соединение с веб-сокетом закрывается, поэтому 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 минут браузер зависает. Я один такой?
Marc

16
Вы должны добавить «ws = null;» перед setTimeout (), чтобы избежать умножения объектов ws и eventHandligs
Макс

8
Поправьте меня, если я ошибаюсь, но этот код опасен, так как определенное количество отключений вызовет переполнение стека. Это потому, что вы вызываете startрекурсивно, никогда не возвращаясь.
Forivin 03

11
@Forivin Здесь нет проблемы с переполнением стека. Поскольку в Javascript есть только один поток, выполняющий наш код в любой момент, setTimeout () планирует выполнение переданной функции в будущем, когда этот единственный поток снова станет свободным. После вызова здесь setTimeout () поток возвращается из функции (очищая стек), а затем переходит к обработке следующего события в очереди. В конечном итоге он перейдет к нашей анонимной функции, которая вызывает start и которая будет вызываться как верхний фрейм в стеке.
Stinky

40

Решение, данное Эндрю, работает не идеально, потому что в случае потери соединения сервер может отправить несколько событий закрытия.

В этом случае вы установите несколько setTimout. Решение, данное Эндрю, может работать только в том случае, если сервер будет готов до пяти секунд.

Затем, на основе переделанного решения Эндрю, я использовал 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если примените к ним идею "глобального таймера id";)
RozzA

3
«Решение, данное Эндрю, может работать только в том случае, если сервер будет готов до пяти секунд». - Утверждение не соответствует действительности. Если сервер по-прежнему недоступен через пять секунд, ваш клиент не сможет открыть соединение WebSocket, и oncloseсобытие будет запущено снова.
Сурав Гош

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

ReconnectingWebSocket не обрабатывает повторные подключения с использованием этого алгоритма.


Отличный ответ, особенно потому, что в нем упоминается риск высокой нагрузки на сервер, когда сервер закрывает соединения с веб-сокетами, и все клиенты (которых могут быть сотни или тысячи) пытаются повторно подключиться одновременно. Вместо экспоненциального отката вы также можете рандомизировать задержку, например, между 0 и 10 секундами. Это также распределит нагрузку на сервер.
Jochem Schulenklopper

29

Я использую этот шаблон некоторое время для чистого ванильного 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 секунд.

Поэтому, если сервер не работает, когда это выполняется или во время события onclose, соединение все равно вернется, как только оно вернется в онлайн.

ПРИМЕЧАНИЕ. Использование этого сценария не позволит вам когда-либо прекратить попытки открыть соединение ... но я думаю, что вы этого хотите?


7
Я бы только изменил: function check () {if (! Ws || ws.readyState === WebSocket.CLOSED) start (); }
dieresys

1
Этот подход, плюс описанная здесь техника поддержания активности , кажется мне хорошо работает.
Питер

@Peter, не уверен, открыто ли состояние ws, вам нужно (или нужно) пинговать, если я прав, он уже в протоколе websocket. Это излишнее убийство только что привело к нагрузке на ваш сервер ...
конте

@comte некоторые ws-серверы отключают вас после «периода простоя», когда сообщения от клиента не отправляются, поэтому для сохранения соединения пинг - это злая необходимость.
RozzA

2

Ниже приведены коды, которые я использовал в своем проекте, которые работают на 100%.

  1. Поместите весь код веб-сокета в функцию init.
  2. Внутри обратного вызова onclose снова вызовите init.
  3. Наконец, вызовите функцию инициализации внутри функции готовности документа.

var name = sessionStorage.getItem ('имя');

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.