Sambungkan kembali Klien saat server melakukan boot ulang di WebSocket


94

Saya menggunakan soket web menggunakan PHP5 dan browser Chrome sebagai klien. Saya telah mengambil kode dari situs http://code.google.com/p/phpwebsocket/ .

Saya menjalankan server, dan klien juga terhubung. Saya juga bisa mengobrol. Sekarang ketika saya me-restart server (dengan mematikannya dan memulainya lagi), klien mendapatkan informasi yang terputus, tetapi secara otomatis tidak terhubung kembali dengan server ketika saya mengirim pesan.

Bagaimana cara mencapai ini? Seperti ketika saya mendapatkan informasi yang terputus, haruskah saya memeriksanya dan mengirimkannya ke JavaScript untuk menyegarkan halaman atau menghubungkan kembali?

Jawaban:


143

Ketika server reboot, koneksi Web Socket ditutup, sehingga oncloseevent JavaScript dipicu. Berikut adalah contoh yang mencoba menyambungkan kembali setiap lima detik.

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
Saya berharap ada cara yang lebih elegan, tanpa membangun objek baru dan menentukan aksi acara ...
ciembor

3
Setelah 5 menit, browser membeku. Apakah saya satu-satunya?
Marc

17
Anda harus menambahkan "ws = null;" sebelum setTimeout () untuk menghindari perkalian ws objek dan eventHandligs
Maks

8
Koreksi saya jika saya salah, tetapi kode ini agak berbahaya karena sejumlah pemutusan hubungan akan menyebabkan tumpukan melimpah. Itu karena Anda menelepon startsecara rekursif, tanpa pernah kembali.
Forivin

11
@Forivin Tidak ada masalah stackoverflow di sini. Karena hanya ada 1 utas tunggal dalam Javascript yang mengeksekusi kode kita pada saat tertentu, setTimeout () menjadwalkan fungsi yang diteruskan untuk dieksekusi di masa depan ketika utas tunggal itu bebas lagi. Setelah setTimeout () dipanggil di sini, utas kembali dari fungsi (membersihkan tumpukan), lalu melanjutkan untuk memproses acara berikutnya dalam antrian. Ini pada akhirnya akan sampai ke fungsi anonim kami yang panggilannya dimulai dan yang akan disebut sebagai bingkai teratas dalam tumpukan.
Stinky

40

Solusi yang diberikan oleh Andrew tidak berfungsi sempurna karena, jika koneksi terputus, server mungkin mengirim beberapa peristiwa penutupan.

Dalam hal ini, Anda akan menyetel beberapa setTimout. Solusi yang diberikan oleh Andrew hanya dapat berfungsi jika server siap sebelum lima detik.

Kemudian, berdasarkan solusi Andrew, yang dikerjakan ulang, saya telah memanfaatkan setInterval yang melampirkan ID ke objek jendela (dengan cara itu ia tersedia "di mana saja"):

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

Anda masih dapat menggunakan setTimeoutjika Anda menerapkan ide "global timer id" untuk mereka;)
RozzA

3
"Solusi yang diberikan oleh Andrew hanya dapat berfungsi jika server siap sebelum lima detik." - Pernyataan tersebut tidak benar. Jika server masih tidak tersedia setelah lima detik, klien Anda akan gagal membuka koneksi WebSocket dan oncloseacara akan dijalankan lagi.
Sourav Ghosh

36

ReconnectingWebSocket

GitHub menghosting pustaka JavaScript kecil yang menghiasi API WebSocket untuk menyediakan koneksi WebSocket yang secara otomatis akan terhubung kembali jika koneksi terputus.

Pustaka yang diperkecil dengan kompresi gzip berukuran kurang dari 600 byte.

Repositori resmi tersedia di sini:

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

Banjir server

Jika sejumlah besar klien terhubung ke server saat reboot. Mungkin bermanfaat untuk mengelola pengaturan waktu menghubungkan kembali klien dengan menggunakan algoritma Backoff Eksponensial.

Algoritme bekerja seperti ini:

  1. Untuk k upaya, buat interval waktu acak antara 0 dan 2 ^ k - 1,
  2. Jika Anda dapat menyambungkan kembali, setel ulang k ke 1,
  3. Jika koneksi ulang gagal, k bertambah 1 dan proses dimulai ulang pada langkah 1,
  4. Untuk memotong interval maks, ketika sejumlah percobaan k telah dicapai, k berhenti meningkat setelah setiap percobaan.

Referensi:

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

ReconnectingWebSocket tidak menangani koneksi ulang dengan menggunakan algoritma ini.


Jawaban yang bagus, terutama karena ia menyebutkan risiko beban server yang tinggi setelah server menutup koneksi soket web, dan semua klien (yang dapat berjumlah ratusan atau ribuan) mencoba menyambung kembali pada saat yang bersamaan. Alih-alih kemunduran eksponensial, Anda juga dapat mengacak penundaan seperti antara 0 dan 10 detik. Itu akan menyebarkan beban di server juga.
Jochem Schulenklopper

29

Saya telah menggunakan patten ini untuk sementara waktu untuk vanilla JavaScript murni, dan ini mendukung lebih banyak kasus daripada jawaban lainnya.

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


});

Ini akan mencoba lagi segera setelah server menutup koneksi, dan akan memeriksa koneksi untuk memastikannya juga setiap 5 detik.

Jadi jika server tidak aktif saat ini berjalan atau pada saat acara onclose, koneksi akan tetap kembali setelah kembali online.

CATATAN: Menggunakan skrip ini tidak akan memungkinkan Anda untuk berhenti mencoba membuka koneksi ... tetapi saya pikir itu yang Anda inginkan?


7
Saya hanya akan mengubah: function check () {if (! Ws || ws.readyState === WebSocket.CLOSED) start (); }
dieresys

1
Pendekatan ini, ditambah teknik menjaga-hidup yang dijelaskan di sini , tampaknya bekerja dengan baik untuk saya.
Peter

@ Peter, tidak yakin apakah ws state terbuka Anda perlu (atau harus) ping, jika saya benar itu sudah dalam protokol websocket. Pembunuhan ini baru saja dimuat di server Anda ...
comte

@comte beberapa server ws memutuskan koneksi Anda setelah 'periode idle' tidak ada pesan yang dikirim dari klien & untuk menjaga koneksi tetap terbuka, ping adalah kebutuhan yang jahat.
RozzA

2

Di bawah ini adalah kode yang saya gunakan dalam proyek saya yang bekerja 100%.

  1. Letakkan semua kode websocket di dalam fungsi init.
  2. Di dalam callback onclose, panggil init lagi.
  3. Terakhir, panggil fungsi init di dalam fungsi siap dokumen.

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

Tidak dapat berkomentar, tetapi yang berikut:

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

Ditambah lagi https://www.npmjs.com/package/back udah cukup bagus :)


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

Akhirnya, saya membuat ws auto reconnect di vue + ts, mirip seperti berikut:

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

Acara penutupan sisi klien untuk WebSocket memiliki properti wasClean , yang berguna bagi saya. Sepertinya ini disetel ke true dalam kasus di mana komputer klien masuk ke mode tidur dll. Atau ketika server tiba-tiba berhenti dll. Ini disetel ke false jika Anda menutup soket secara manual, dalam hal ini Anda tidak ingin membukanya soket secara otomatis kembali. Di bawah kode dari proyek Angular 7. Saya memiliki kode ini dalam sebuah layanan, jadi dapat digunakan dari komponen apa pun.

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

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.