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.