WebSocket에서 서버 재부팅시 클라이언트 재 연결


93

PHP5와 Chrome 브라우저를 클라이언트로 사용하는 웹 소켓을 사용하고 있습니다. http://code.google.com/p/phpwebsocket/ 사이트에서 코드를 가져 왔습니다 .

나는 서버를 실행하고 클라이언트도 연결됩니다. 채팅도 할 수 있습니다. 이제 서버를 다시 시작하면 (종료하고 다시 시작하여) 클라이언트는 연결이 끊어진 정보를 얻지 만 메시지를 보낼 때 자동으로 서버에 다시 연결하지 않습니다.

이것을 달성하는 방법? 연결이 끊긴 정보를 받았을 때처럼 확인하고 JavaScript로 보내 페이지를 새로 고치거나 다시 연결해야합니까?

답변:


143

서버가 재부팅되면 웹 소켓 연결이 닫히고 JavaScript onclose이벤트가 트리거됩니다. 다음은 5 초마다 다시 연결을 시도하는 예입니다.

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;"을 추가해야합니다. 객체와 eventHandligs의 곱셈을 피하기 위해 setTimeout () 전에
— Max

8
내가 틀렸다면 정정하십시오.하지만 특정 양의 연결이 끊어지면 스택 오버플로가 발생 하므로이 코드는 위험합니다. start반환하지 않고 재귀 적으로 호출하기 때문 입니다.
— Forivin

10
@Forivin 여기에 stackoverflow 문제가 없습니다. 주어진 순간에 우리 코드를 실행하는 자바 스크립트에는 단 하나의 스레드 만 있기 때문에 setTimeout ()은 전달 된 함수가 해당 단일 스레드가 다시 사용 가능 해지면 나중에 실행되도록 예약합니다. 여기에서 setTimeout ()이 호출 된 후 스레드는 함수에서 반환 (스택 지우기) 한 다음 큐의 다음 이벤트를 처리합니다. 결국 start를 호출하는 익명 함수에 도달하고 스택의 최상위 프레임으로 호출됩니다.
— Stinky

39

Andrew가 제공 한 솔루션은 완벽하게 작동하지 않습니다. 연결이 끊어진 경우 서버가 여러 개의 닫기 이벤트를 보낼 수 있기 때문입니다.

이 경우 여러 setTimout을 설정합니다. Andrew가 제공 한 솔루션은 서버가 5 초 전에 준비된 경우에만 작동 할 수 있습니다.

그런 다음 Andrew 솔루션을 기반으로 재 작업하여 창 개체에 ID를 연결하는 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
"Andrew가 제공 한 솔루션은 서버가 5 초 전에 준비된 경우에만 작동 할 수 있습니다."-이 진술은 사실이 아닙니다. 5 초 후에도 서버를 사용할 수 없으면 클라이언트가 WebSocket 연결을 열지 못하고 onclose이벤트가 다시 시작됩니다.
— Sourav Ghosh

36

WebSocket 다시 연결

GitHub는 WebSocket API를 장식하는 작은 JavaScript 라이브러리를 호스팅하여 연결이 끊어지면 자동으로 다시 연결되는 WebSocket 연결을 제공합니다.

gzip 압축을 사용하는 축소 된 라이브러리는 600 바이트 미만입니다.

공식 저장소는 여기에서 사용할 수 있습니다.

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

서버 홍수

재부팅 할 때 많은 수의 클라이언트가 서버에 연결되어있는 경우. Exponential Backoff 알고리즘을 사용하여 클라이언트의 재 연결 타이밍을 관리하는 것이 좋습니다.

알고리즘은 다음과 같이 작동합니다.

  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

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 초마다 작동하는지 확인합니다.

따라서 이것이 실행될 때 또는 onclose 이벤트가 발생했을 때 서버가 작동하지 않는 경우 연결이 다시 온라인 상태가되면 다시 연결됩니다.

참고 :이 스크립트를 사용해도 연결 시도를 중단 할 수는 없지만 그게 원하는 것 같습니다.


7
나는 단지 바꿀 것이다 : function check () {if (! ws || ws.readyState === WebSocket.CLOSED) start (); }
— dieresys jul.

1
이 접근 방식과 여기 에 설명 된 연결 유지 기술 이 잘 작동하는 것 같습니다.
— Peter

@Peter, ws 상태가 열려 있는지 확실하지 않습니다. 핑이 필요하거나 핑해야합니다. 정확하면 이미 웹 소켓 프로토콜에 있습니다. 이 과잉으로 인해 서버에 부하가 발생했습니다 ...
— comte

@comte 일부 ws 서버는 클라이언트에서 메시지가 전송되지 않는 '유휴 기간'이 지나면 연결을 끊으므로 연결을 계속 유지하기 위해 핑이 필요합니다.
— RozzA

2

다음은 100 % 작동하는 내 프로젝트에서 사용한 코드입니다.

  1. 모든 websocket 코드를 init 함수 안에 넣으십시오.
  2. onclose 콜백 내에서 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

마지막으로 다음과 같이 vue + ts에서 ws 자동 재 연결을 만듭니다.

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);   
        } 
    }
    .....
    .....
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.