javascript websocket 心跳檢測機制介紹


====測試代碼:

 

==index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <div>
        abc
    </div>
</body>
<script type="text/javascript" src="js/websocket.js"></script>
<script src="js/client.js"></script>
</html>

 

==websocket.js

var lockReconnect = false; //避免ws重復連接
var ws = null; // 判斷當前瀏覽器是否支持WebSocket
var wsUrl = null;
var config = {};



function socketLink(set) {
    config = set;
    wsUrl = config.url;
    createWebSocket(wsUrl); //連接ws
}

function createWebSocket(url) {
    try {
        if ('WebSocket' in window) {
            ws = new WebSocket(url, 'echo-protocol');
        } else if ('MozWebSocket' in window) {
            ws = new MozWebSocket(url, 'echo-protocol');
        } else {
            alert("您的瀏覽器不支持websocket")
        }
        initEventHandle();
    } catch (e) {
        reconnect(url);
        console.log(e);
    }
}

function initEventHandle() {
    ws.onclose = function() {
        reconnect(wsUrl);
        console.log("llws連接關閉!" + new Date().toUTCString());
    };
    ws.onerror = function() {
        reconnect(wsUrl);
        console.log("llws連接錯誤!");
    };
    ws.onopen = function() {
        heartCheck.reset().start(); //心跳檢測重置
        console.log("llws連接成功!" + new Date().toUTCString());
        config.open(ws)
    };
    ws.onmessage = function(event) { //如果獲取到消息,心跳檢測重置
        heartCheck.reset().start(); //拿到任何消息都說明當前連接是正常的
        config.msg(event.data,ws)
    };
}
// 監聽窗口關閉事件,當窗口關閉時,主動去關閉websocket連接,防止連接還沒斷開就關閉窗口,server端會拋異常。
window.onbeforeunload = function() {
    ws.close();
}

function reconnect(url) {
    if (lockReconnect) return;
    lockReconnect = true;
    setTimeout(function() { //沒連接上會一直重連,設置延遲避免請求過多
        createWebSocket(url);
        lockReconnect = false;
    }, 2000);
}

//心跳檢測
var heartCheck = {
    timeout: 10000, //9分鍾發一次心跳
    timeoutObj: null,
    serverTimeoutObj: null,
    reset: function() {
        clearTimeout(this.timeoutObj);
        clearTimeout(this.serverTimeoutObj);
        return this;
    },
    start: function() {
        var self = this;
        this.timeoutObj = setTimeout(function() {
            //這里發送一個心跳,后端收到后,返回一個心跳消息,
            //onmessage拿到返回的心跳就說明連接正常
            ws.send("ping");
            console.log("ping!")
            self.serverTimeoutObj = setTimeout(function() { //如果超過一定時間還沒重置,說明后端主動斷開了
                console.log("try=close")
                ws.close(); //這里為什么要在send檢測消息后,倒計時執行這個代碼呢,因為這個代碼的目的時為了觸發onclose方法,這樣才能實現onclose里面的重連方法
          //所以這個代碼也很重要,沒有這個方法,有些時候發了定時檢測消息給后端,后端超時(我們自己設定的時間)后,不會自動觸發onclose方法。我們只有執行ws.close()代碼,讓ws觸發onclose方法
          //的執行。如果沒有這個代碼,連接沒有斷線的情況下而后端沒有正常檢測響應,那么瀏覽器時不會自動超時關閉的(比如谷歌瀏覽器),谷歌瀏覽器會自動觸發onclose
          //是在斷網的情況下,在沒有斷線的情況下,也就是后端響應不正常的情況下,瀏覽器不會自動觸發onclose,所以需要我們自己設定超時自動觸發onclose,這也是這個代碼的
          //的作用。
}, self.timeout) }, this.timeout) } }

心跳檢測的目的時什么呢?

一個是為了定時發送消息,使連接不超時自動斷線,可能后端設了超時時間就會自動斷線,所以需要定時發送消息給后端,讓后端服務器知道連接還在通消息不能斷。

二來是為了檢測在正常還連接着的情況下,判斷后端是否正常,如果我們發了一個定時檢測給后端,后端按照約定要下發一個檢測消息給前端,這樣才是正常的。

可是如果后端沒有正常下發呢,我們就要設定一下超時要重連了,我們把重連寫在了onclose里面,當時正常連接的情況下,超時未能響應檢測消息的情況下,瀏覽器不會自動斷掉觸發onclose,所以需要我們手動觸發,也就是在超時里寫一個ws.close()讓ws關閉觸發onclose方法,實現重連。

==瀏覽器會觸發onclose的情況是斷網斷線的情況下,當時正常連接還未斷線,瀏覽器時不會自動觸發onclose的,所以就有超時手動觸發onclose的必要。

self.serverTimeoutObj = setTimeout(function() { //如果超過一定時間還沒重置,說明后端主動斷開了
                console.log("try=close")
                ws.close(); //這里為什么要在send檢測消息后,倒計時執行這個代碼呢,因為這個代碼的目的時為了觸發onclose方法,這樣才能實現onclose里面的重連方法
          //所以這個代碼也很重要,沒有這個方法,有些時候發了定時檢測消息給后端,后端超時(我們自己設定的時間)后,不會自動觸發onclose方法。我們只有執行ws.close()代碼,讓ws觸發onclose方法
           //的執行。如果沒有這個代碼,連接沒有斷線的情況下而后端沒有正常檢測響應,那么瀏覽器時不會自動超時關閉的(比如谷歌瀏覽器),谷歌瀏覽器會自動觸發onclose
          //是在斷網的情況下,在沒有斷線的情況下,也就是后端響應不正常的情況下,瀏覽器不會自動觸發onclose,所以需要我們自己設定超時自動觸發onclose,這也是這個代碼的
          //的作用。

            }, self.timeout)
ws.onopen = function() {
        heartCheck.reset().start(); //心跳檢測重置   在open的時候觸發心跳檢測
        console.log("llws連接成功!" + new Date().toUTCString());
        config.open(ws)
    };
    ws.onmessage = function(event) { //如果獲取到消息,心跳檢測重置
        heartCheck.reset().start(); //拿到任何消息都說明當前連接是正常的  //如果后端有下發消息,那么就會重置初始化心跳檢測,除非超時沒下發,那么就會觸發onclose
       //然后觸發重連
config.msg(event.data,ws) };

 

 

==client.js

var number=0;
var config = {
    url: 'ws://localhost:8080/',
    open: (ws) => {
        function sendNumber() {
            if (ws.readyState === ws.OPEN) {
                number = number+1;
                ws.send(number.toString());
                setTimeout(sendNumber, 1000);
            }
        }
        sendNumber()
    },
    msg: (data) => {
        console.log(data, "msg")
    }
}
socketLink(config)

 

 

==node 后端測試代碼:

#!/usr/bin/env node

var WebSocketServer = require('websocket').server;
var http = require('http');

var server = http.createServer(function(request, response) {
    console.log((new Date()) + ' Received request for ' + request.url);
    response.writeHead(404);
    response.end();
});
server.listen(8080, function() {
    console.log((new Date()) + ' Server is listening on port 8080');
});

wsServer = new WebSocketServer({
    httpServer: server,
    // You should not use autoAcceptConnections for production
    // applications, as it defeats all standard cross-origin protection
    // facilities built into the protocol and the browser.  You should
    // *always* verify the connection's origin and decide whether or not
    // to accept it.
    autoAcceptConnections: false
});

function originIsAllowed(origin) {
    console.log(origin,"origin")
    // put logic here to detect whether the specified origin is allowed.
    return true;
}

wsServer.on('request', function(request) {
    if (!originIsAllowed(request.origin)) {
        // Make sure we only accept requests from an allowed origin
        request.reject();
        console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.');
        return;
    }

    var connection = request.accept('echo-protocol', request.origin);
    console.log((new Date()) + ' Connection accepted.');
    connection.on('message', function(message) {
        if (message.type === 'utf8') {
            console.log('Received Message: ' + message.utf8Data);
            //connection.sendUTF(message.utf8Data);  //注釋掉是為了測試前端在沒后端響應卻連接着的情況下,是什么反應。
        } else if (message.type === 'binary') {
            console.log('Received Binary Message of ' + message.binaryData.length + ' bytes');
            connection.sendBytes(message.binaryData);
        }
    });
    connection.on('close', function(reasonCode, description) {
        console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
    });
});

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM