欧美成人精品手机在线观看_69视频国产_动漫精品第一页_日韩中文字幕网 - 日本欧美一区二区

LOGO OA教程 ERP教程 模切知識(shí)交流 PMS教程 CRM教程 開(kāi)發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

【W(wǎng)EB開(kāi)發(fā)】websocket心跳保持連接機(jī)制

admin
2025年3月31日 18:17 本文熱度 214

心跳機(jī)制在 WebSocket 通信中是一種常用的技術(shù),用于維持連接的穩(wěn)定性、檢測(cè)連接是否正常。以下為你詳細(xì)介紹在前端使用 WebSocket 時(shí)如何實(shí)現(xiàn)心跳機(jī)制,以及相關(guān)代碼示例。

實(shí)現(xiàn)思路

  • 發(fā)送心跳包:客戶(hù)端定期向服務(wù)器發(fā)送一個(gè)特定格式的消息(心跳包),以表明自己處于活躍狀態(tài)。
  • 接收響應(yīng):服務(wù)器收到心跳包后,返回一個(gè)響應(yīng)消息,客戶(hù)端通過(guò)檢查是否收到響應(yīng)來(lái)判斷連接是否正常。
  • 超時(shí)處理:如果客戶(hù)端在一定時(shí)間內(nèi)沒(méi)有收到服務(wù)器的響應(yīng),認(rèn)為連接可能出現(xiàn)問(wèn)題,嘗試重新連接。

示例代碼

class WebSocketClient {
    constructor(url) {
        this.url = url;
        this.socket = null;
        this.reconnectInterval = 5000; // 重連間隔時(shí)間,單位:毫秒
        this.reconnectTimer = null;
        this.messageHandlers = [];
        this.errorHandlers = [];
        this.openHandlers = [];
        this.closeHandlers = [];

        this.heartbeatInterval = 3000; // 心跳間隔時(shí)間,單位:毫秒
        this.heartbeatTimer = null;
        this.lastHeartbeatResponseTime = null;
        this.heartbeatTimeout = 5000; // 心跳超時(shí)時(shí)間,單位:毫秒
        this.heartbeatTimeoutTimer = null;

        this.tryConnect();
    }

    // 嘗試建立連接
    tryConnect() {
        this.socket = new WebSocket(this.url);

        this.socket.onopen = () => {
            console.log('WebSocket 連接已建立');
            clearInterval(this.reconnectTimer);
            this.openHandlers.forEach(handler => handler());
            this.startHeartbeat();
        };

        this.socket.onmessage = (event) => {
            if (event.data === 'heartbeat_response') {
                this.lastHeartbeatResponseTime = Date.now();
                clearTimeout(this.heartbeatTimeoutTimer);
                this.heartbeatTimeoutTimer = setTimeout(() => {
                    this.handleHeartbeatTimeout();
                }, this.heartbeatTimeout);
            } else {
                this.messageHandlers.forEach(handler => handler(event.data));
            }
        };

        this.socket.onerror = (error) => {
            console.error('WebSocket 連接出錯(cuò):', error);
            this.errorHandlers.forEach(handler => handler(error));
            this.reconnect();
        };

        this.socket.onclose = (event) => {
            console.log('WebSocket 連接已關(guān)閉,代碼:', event.code, '原因:', event.reason);
            this.closeHandlers.forEach(handler => handler(event));
            this.reconnect();
            this.stopHeartbeat();
        };
    }

    // 重連機(jī)制
    reconnect() {
        if (!this.reconnectTimer) {
            this.reconnectTimer = setInterval(() => {
                console.log('嘗試重新連接 WebSocket...');
                this.tryConnect();
            }, this.reconnectInterval);
        }
    }

    // 發(fā)送消息
    sendMessage(message) {
        if (this.socket && this.socket.readyState === WebSocket.OPEN) {
            this.socket.send(message);
        } else {
            console.error('無(wú)法發(fā)送消息,WebSocket 未連接');
        }
    }

    // 添加消息處理函數(shù)
    onMessage(handler) {
        this.messageHandlers.push(handler);
    }

    // 添加錯(cuò)誤處理函數(shù)
    onError(handler) {
        this.errorHandlers.push(handler);
    }

    // 添加連接打開(kāi)處理函數(shù)
    onOpen(handler) {
        this.openHandlers.push(handler);
    }

    // 添加連接關(guān)閉處理函數(shù)
    onClose(handler) {
        this.closeHandlers.push(handler);
    }

    // 關(guān)閉連接
    close() {
        if (this.socket) {
            clearInterval(this.reconnectTimer);
            this.socket.close();
            this.stopHeartbeat();
        }
    }

    // 啟動(dòng)心跳機(jī)制
    startHeartbeat() {
        this.heartbeatTimer = setInterval(() => {
            this.sendMessage('heartbeat');
            this.heartbeatTimeoutTimer = setTimeout(() => {
                this.handleHeartbeatTimeout();
            }, this.heartbeatTimeout);
        }, this.heartbeatInterval);
    }

    // 停止心跳機(jī)制
    stopHeartbeat() {
        clearInterval(this.heartbeatTimer);
        clearTimeout(this.heartbeatTimeoutTimer);
    }

    // 處理心跳超時(shí)
    handleHeartbeatTimeout() {
        console.log('心跳超時(shí),嘗試重新連接...');
        this.socket.close();
    }
}

// 使用示例
const socketClient = new WebSocketClient('ws://echo.websocket.org');

// 監(jiān)聽(tīng)消息
socketClient.onMessage((message) => {
    console.log('收到消息:', message);
});

// 監(jiān)聽(tīng)連接打開(kāi)
socketClient.onOpen(() => {
    console.log('連接已打開(kāi)');
});

// 監(jiān)聽(tīng)錯(cuò)誤
socketClient.onError((error) => {
    console.error('發(fā)生錯(cuò)誤:', error);
});

// 監(jiān)聽(tīng)連接關(guān)閉
socketClient.onClose((event) => {
    console.log('連接關(guān)閉:', event);
});

代碼解釋

  1. 構(gòu)造函數(shù):除了初始化基本的 WebSocket 連接參數(shù)外,還初始化了心跳機(jī)制相關(guān)的參數(shù),如心跳間隔時(shí)間 heartbeatInterval、心跳超時(shí)時(shí)間 heartbeatTimeout 等。
  2. startHeartbeat 方法:使用 setInterval 定時(shí)發(fā)送心跳包(heartbeat),并啟動(dòng)一個(gè)定時(shí)器 heartbeatTimeoutTimer 用于檢測(cè)心跳超時(shí)。
  3. onmessage 事件處理:當(dāng)收到服務(wù)器的心跳響應(yīng)消息(heartbeat_response)時(shí),更新最后一次收到響應(yīng)的時(shí)間 lastHeartbeatResponseTime,并重置心跳超時(shí)定時(shí)器。
  4. handleHeartbeatTimeout 方法:當(dāng)心跳超時(shí)發(fā)生時(shí),關(guān)閉當(dāng)前的 WebSocket 連接,觸發(fā)重連機(jī)制。
  5. stopHeartbeat 方法:停止心跳定時(shí)器和超時(shí)定時(shí)器。

通過(guò)上述代碼,你可以在前端 WebSocket 通信中實(shí)現(xiàn)一個(gè)簡(jiǎn)單的心跳機(jī)制,確保連接的穩(wěn)定性。同時(shí),服務(wù)器端也需要相應(yīng)地處理心跳包并返回響應(yīng)消息。

分享

用ping\pong來(lái)實(shí)現(xiàn)

在 WebSocket 通信里,ping 和 pong 是標(biāo)準(zhǔn)的心跳機(jī)制實(shí)現(xiàn)方式。ping 是客戶(hù)端或服務(wù)器發(fā)送的探測(cè)消息,pong 則是對(duì) ping 的響應(yīng)。下面是一個(gè)使用 JavaScript 封裝 WebSocket 并利用 ping/pong 實(shí)現(xiàn)心跳機(jī)制的詳細(xì)示例。

實(shí)現(xiàn)思路

  • 客戶(hù)端:周期性地發(fā)送 ping 消息,同時(shí)監(jiān)聽(tīng) pong 響應(yīng),若超時(shí)未收到 pong 則認(rèn)為連接異常。
  • 服務(wù)器端:接收到 ping 消息后,立即返回 pong 響應(yīng)。

前端代碼示例

class WebSocketWithPingPong {
    constructor(url) {
        this.url = url;
        this.socket = null;
        this.pingInterval = 5000; // 發(fā)送 ping 消息的間隔時(shí)間(毫秒)
        this.pongTimeout = 3000; // 等待 pong 響應(yīng)的超時(shí)時(shí)間(毫秒)
        this.pingTimer = null;
        this.pongTimeoutTimer = null;
        this.reconnectInterval = 5000; // 重連間隔時(shí)間(毫秒)
        this.reconnectTimer = null;
        this.messageHandlers = [];
        this.errorHandlers = [];
        this.openHandlers = [];
        this.closeHandlers = [];

        this.connect();
    }

    connect() {
        this.socket = new WebSocket(this.url);

        this.socket.onopen = () => {
            console.log('WebSocket 連接已建立');
            clearInterval(this.reconnectTimer);
            this.openHandlers.forEach(handler => handler());
            this.startPing();
        };

        this.socket.onmessage = (event) => {
            if (event.data === 'pong') {
                clearTimeout(this.pongTimeoutTimer);
            } else {
                this.messageHandlers.forEach(handler => handler(event.data));
            }
        };

        this.socket.onerror = (error) => {
            console.error('WebSocket 連接出錯(cuò):', error);
            this.errorHandlers.forEach(handler => handler(error));
            this.reconnect();
        };

        this.socket.onclose = (event) => {
            console.log('WebSocket 連接已關(guān)閉,代碼:', event.code, '原因:', event.reason);
            this.closeHandlers.forEach(handler => handler(event));
            this.reconnect();
            this.stopPing();
        };
    }

    reconnect() {
        if (!this.reconnectTimer) {
            this.reconnectTimer = setInterval(() => {
                console.log('嘗試重新連接 WebSocket...');
                this.connect();
            }, this.reconnectInterval);
        }
    }

    sendMessage(message) {
        if (this.socket && this.socket.readyState === WebSocket.OPEN) {
            this.socket.send(message);
        } else {
            console.error('無(wú)法發(fā)送消息,WebSocket 未連接');
        }
    }

    onMessage(handler) {
        this.messageHandlers.push(handler);
    }

    onError(handler) {
        this.errorHandlers.push(handler);
    }

    onOpen(handler) {
        this.openHandlers.push(handler);
    }

    onClose(handler) {
        this.closeHandlers.push(handler);
    }

    close() {
        if (this.socket) {
            clearInterval(this.reconnectTimer);
            this.socket.close();
            this.stopPing();
        }
    }

    startPing() {
        this.pingTimer = setInterval(() => {
            this.sendMessage('ping');
            this.pongTimeoutTimer = setTimeout(() => {
                this.handlePongTimeout();
            }, this.pongTimeout);
        }, this.pingInterval);
    }

    stopPing() {
        clearInterval(this.pingTimer);
        clearTimeout(this.pongTimeoutTimer);
    }

    handlePongTimeout() {
        console.log('未收到 pong 響應(yīng),嘗試重新連接...');
        this.socket.close();
    }
}

// 使用示例
const socket = new WebSocketWithPingPong('ws://your-websocket-server-url');

socket.onOpen(() => {
    console.log('連接已打開(kāi)');
});

socket.onMessage((message) => {
    console.log('收到消息:', message);
});

socket.onError((error) => {
    console.error('發(fā)生錯(cuò)誤:', error);
});

socket.onClose((event) => {
    console.log('連接關(guān)閉:', event);
});

代碼解釋

  1. 構(gòu)造函數(shù):初始化 WebSocket 連接所需的參數(shù),包括 ping 發(fā)送間隔、pong 超時(shí)時(shí)間、重連間隔等,并且調(diào)用 connect 方法開(kāi)始建立連接。
  2. connect 方法:創(chuàng)建 WebSocket 實(shí)例,為 onopenonmessageonerror 和 onclose 事件綁定處理函數(shù)。連接建立成功后,啟動(dòng) ping 發(fā)送機(jī)制。
  3. startPing 方法:使用 setInterval 周期性地發(fā)送 ping 消息,同時(shí)啟動(dòng)一個(gè) pong 超時(shí)定時(shí)器。
  4. onmessage 事件處理:當(dāng)收到 pong 響應(yīng)時(shí),清除 pong 超時(shí)定時(shí)器;若收到其他消息,則調(diào)用注冊(cè)的消息處理函數(shù)。
  5. handlePongTimeout 方法:若在 pong 超時(shí)時(shí)間內(nèi)未收到 pong 響應(yīng),關(guān)閉當(dāng)前連接并觸發(fā)重連機(jī)制。
  6. 其他方法:如 sendMessage 用于發(fā)送消息,onMessageonErroronOpen 和 onClose 用于注冊(cè)相應(yīng)的事件處理函數(shù),close 用于關(guān)閉連接,stopPing 用于停止 ping 發(fā)送和 pong 超時(shí)檢測(cè)。

服務(wù)器端示例(使用 Node.js 和 ws 庫(kù))

const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', (ws) => {
    ws.on('message', (message) => {
        if (message === 'ping') {
            ws.send('pong');
        } else {
            // 處理其他消息
            console.log('收到消息:', message);
        }
    });
});

console.log('WebSocket 服務(wù)器已啟動(dòng),監(jiān)聽(tīng)端口 8080');

服務(wù)器端代碼解釋

  • 使用 ws 庫(kù)創(chuàng)建一個(gè) WebSocket 服務(wù)器,監(jiān)聽(tīng) 8080 端口。
  • 當(dāng)有客戶(hù)端連接時(shí),監(jiān)聽(tīng) message 事件,若收到 ping 消息,立即返回 pong 響應(yīng);若收到其他消息,則進(jìn)行相應(yīng)處理。

作者:coding_time
鏈接:https://juejin.cn/post/7475896691884900390
來(lái)源:稀土掘金
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。

該文章在 2025/4/1 12:59:13 編輯過(guò)
關(guān)鍵字查詢(xún)
相關(guān)文章
正在查詢(xún)...
點(diǎn)晴ERP是一款針對(duì)中小制造業(yè)的專(zhuān)業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國(guó)內(nèi)大量中小企業(yè)的青睞。
點(diǎn)晴PMS碼頭管理系統(tǒng)主要針對(duì)港口碼頭集裝箱與散貨日常運(yùn)作、調(diào)度、堆場(chǎng)、車(chē)隊(duì)、財(cái)務(wù)費(fèi)用、相關(guān)報(bào)表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點(diǎn),圍繞調(diào)度、堆場(chǎng)作業(yè)而開(kāi)發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類(lèi)企業(yè)的高效ERP管理信息系統(tǒng)。
點(diǎn)晴WMS倉(cāng)儲(chǔ)管理系統(tǒng)提供了貨物產(chǎn)品管理,銷(xiāo)售管理,采購(gòu)管理,倉(cāng)儲(chǔ)管理,倉(cāng)庫(kù)管理,保質(zhì)期管理,貨位管理,庫(kù)位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號(hào)管理軟件。
點(diǎn)晴免費(fèi)OA是一款軟件和通用服務(wù)都免費(fèi),不限功能、不限時(shí)間、不限用戶(hù)的免費(fèi)OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved