Kết nối lại máy khách khi máy chủ khởi động lại trong WebSocket


93

Tôi đang sử dụng ổ cắm web bằng PHP5 và trình duyệt Chrome làm ứng dụng khách. Tôi đã lấy mã từ trang web http://code.google.com/p/phpwebsocket/ .

Tôi chạy máy chủ và máy khách cũng được kết nối. Tôi cũng có thể trò chuyện. Bây giờ khi tôi khởi động lại máy chủ (bằng cách giết nó và khởi động lại), máy khách nhận được thông tin bị ngắt kết nối, nhưng tự động không kết nối lại với máy chủ khi tôi gửi tin nhắn.

Làm thế nào để đạt được điều này? Giống như khi tôi nhận được thông tin bị ngắt kết nối, tôi có nên kiểm tra và gửi nó tới JavaScript để làm mới trang hay kết nối lại không?

Câu trả lời:


143

Khi máy chủ khởi động lại, kết nối Web Socket bị đóng, do đó, onclosesự kiện JavaScript được kích hoạt. Đây là một ví dụ cố gắng kết nối lại sau mỗi năm giây.

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
Tôi hy vọng có cách thanh lịch hơn, mà không cần xây dựng một đối tượng mới và xác định hành động sự kiện ...
ciembor

3
Sau 5 phút, trình duyệt bị đóng băng. Tôi là người duy nhất à?
Marc

16
Bạn nên thêm "ws = null;" trước setTimeout () để tránh nhân các đối tượng ws và eventHandligs
Tối đa

8
Hãy sửa cho tôi nếu tôi sai, nhưng mã này khá nguy hiểm vì một số lượng ngắt kết nối nhất định sẽ gây ra tràn ngăn xếp. Đó là bởi vì bạn gọi startđệ quy, mà không bao giờ quay trở lại.
Forivin

10
@Forivin Không có vấn đề ngăn xếp dòng chảy ở đây. Vì chỉ có 1 luồng duy nhất trong Javascript thực thi mã của chúng ta tại bất kỳ thời điểm nhất định nào, setTimeout () lên lịch hàm đã truyền sẽ được thực thi trong tương lai khi luồng đó rảnh trở lại. Sau khi setTimeout () được gọi ở đây, luồng trả về từ hàm (xóa ngăn xếp), sau đó chuyển sang xử lý sự kiện tiếp theo trong hàng đợi. Cuối cùng nó sẽ đến hàm ẩn danh của chúng ta để bắt đầu các cuộc gọi và đó sẽ được gọi là khung trên cùng trong ngăn xếp.
Stinky

39

Giải pháp do Andrew đưa ra không hoạt động hoàn hảo vì trong trường hợp mất kết nối, máy chủ có thể gửi một số sự kiện gần.

Trong trường hợp đó, bạn sẽ đặt một số setTimout. Giải pháp mà Andrew đưa ra chỉ có thể hoạt động nếu máy chủ sẵn sàng trước năm giây.

Sau đó, dựa trên giải pháp Andrew, được làm lại, tôi đã sử dụng setInterval gắn ID vào đối tượng cửa sổ (theo cách đó, nó có sẵn "ở mọi nơi"):

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

bạn vẫn có thể sử dụng setTimeoutnếu bạn áp dụng ý tưởng "id bộ hẹn giờ chung" cho chúng;)
RozzA

3
“Giải pháp mà Andrew đưa ra chỉ có thể hoạt động nếu máy chủ sẵn sàng trước năm giây.” - Tuyên bố này không đúng. Nếu máy chủ vẫn không khả dụng sau năm giây, máy khách của bạn sẽ không mở được kết nối WebSocket và onclosesự kiện sẽ được kích hoạt lại.
Sourav Ghosh

36

ReconnectingWebSocket

GitHub lưu trữ một thư viện JavaScript nhỏ trang trí API WebSocket để cung cấp kết nối WebSocket sẽ tự động kết nối lại nếu kết nối bị ngắt.

Thư viện thu nhỏ với nén gzip nhỏ hơn 600 byte.

Kho lưu trữ chính thức có sẵn tại đây:

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

Máy chủ lũ lụt

Nếu một số lượng lớn máy khách được kết nối với máy chủ khi nó khởi động lại. Có thể đáng giá khi quản lý thời gian kết nối lại của các khách hàng bằng cách sử dụng thuật toán Backoff theo cấp số nhân.

Thuật toán hoạt động như sau:

  1. Đối với k lần thử, hãy tạo một khoảng thời gian ngẫu nhiên từ 0 đến 2 ^ k - 1,
  2. Nếu bạn có thể kết nối lại, hãy đặt lại k thành 1,
  3. Nếu kết nối lại không thành công, k tăng lên 1 và quá trình khởi động lại ở bước 1,
  4. Để cắt ngắn khoảng thời gian tối đa, khi đã đạt đến số lần thử nhất định, k ngừng tăng sau mỗi lần thử.

Tài liệu tham khảo:

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

ReconnectingWebSocket không xử lý các kết nối lại bằng cách sử dụng thuật toán này.


Câu trả lời tuyệt vời, đặc biệt là vì nó đề cập đến nguy cơ tải máy chủ cao khi máy chủ đóng các kết nối ổ cắm web và tất cả các máy khách (có thể là hàng trăm hoặc hàng nghìn) cố gắng kết nối lại cùng một lúc. Thay vì lùi theo cấp số nhân, bạn cũng có thể ngẫu nhiên hóa độ trễ như từ 0 đến 10 giây. Điều đó cũng sẽ lan truyền tải trên máy chủ.
Jochem Schulenklopper

30

Tôi đã sử dụng patten này được một thời gian cho JavaScript vani thuần túy và nó hỗ trợ nhiều trường hợp hơn các câu trả lời khác.

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


});

Thao tác này sẽ thử lại ngay sau khi máy chủ đóng kết nối và nó sẽ kiểm tra kết nối để đảm bảo rằng nó cũng hoạt động sau mỗi 5 giây.

Vì vậy, nếu máy chủ không hoạt động khi điều này chạy hoặc tại thời điểm diễn ra sự kiện onclose, kết nối sẽ vẫn hoạt động trở lại khi nó trực tuyến trở lại.

LƯU Ý: Sử dụng tập lệnh này sẽ không cho phép bạn ngừng cố gắng mở kết nối ... nhưng tôi nghĩ đó là điều bạn muốn?


7
Tôi chỉ thay đổi: function check () {if (! Ws || ws.readyState === WebSocket.CLOSED) start (); }
dieresys

1
Cách tiếp cận này, cộng với kỹ thuật duy trì sự sống được mô tả ở đây , dường như hoạt động tốt đối với tôi.
Peter

@Peter, không chắc liệu trạng thái ws có đang mở hay không, bạn có cần (hoặc nên) ping hay không, nếu tôi đúng thì nó đã có trong giao thức websocket. Mức quá mức cần thiết này vừa được tải trên máy chủ của bạn ...
comte

@comte một số máy chủ ws ngắt kết nối bạn sau 'thời gian nhàn rỗi' không có tin nhắn nào được gửi từ máy khách và do đó, để giữ cho kết nối mở, ping là một điều cần thiết.
RozzA

2

Dưới đây là các mã tôi đã sử dụng trong dự án của mình, chúng hoạt động 100%.

  1. Đặt tất cả mã websocket bên trong hàm init.
  2. Bên trong callback onclose gọi lại init.
  3. Cuối cùng gọi hàm init bên trong hàm sẵn sàng tài liệu.

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

Không thể nhận xét, nhưng những điều sau:

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

Thêm vào https://www.npmjs.com/package/back là đủ tốt rồi :)


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

Cuối cùng, tôi thực hiện tự động kết nối lại ws trong vue + ts, tương tự như sau:

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

Sự kiện đóng phía máy khách cho WebSocket có thuộc tính wasClean , rất hữu ích đối với tôi. Có vẻ như nó được đặt thành true trong trường hợp máy khách chuyển sang chế độ ngủ, v.v. hoặc khi máy chủ bị dừng đột ngột, v.v. Nó được đặt thành false nếu bạn đóng ổ cắm theo cách thủ công, trong trường hợp đó bạn không muốn mở ổ cắm tự động trở lại. Dưới đây là mã từ một dự án Angular 7. Tôi có mã này trong một dịch vụ, vì vậy nó có thể sử dụng được từ bất kỳ thành phần nào.

    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);   
        } 
    }
    .....
    .....
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.