實現可靠的 WebSocket 連接:心跳與自動重連的最佳實踐

概覽

本文將手把手教你如何從零編寫一個可用于直播或在線聊天的 WSocket 類,依次實現連接建立、心跳檢測、斷線重連、消息收發以及資源清理等功能。我們將結合 WebSocket API 的標準用法、心跳保持重連策略,并充分運用現代 JavaScript 語法(如類、解構、箭頭函數)來確保代碼既高效又易讀。


1. 創建基礎 WebSocket 連接

1.1 引入與初始化

首先,新建一個 WSocket 類,接受目標 URL 和回調函數作為構造參數:

class WSocket {constructor({ url, onMessage, onDisconnect }) {this.url = url;this.onMessage = onMessage;this.onDisconnect = onDisconnect;this.ws = null;this.reconnectCount = 0;}
}
  • url:WebSocket 服務器地址。

  • onMessage/onDisconnect:消息與斷開回調。

1.2 建立連接

在類中添加 connect() 方法,使用原生 API 建立并綁定事件:

connect() {this.ws = new WebSocket(this.url);this.ws.binaryType = 'arraybuffer';                                // 二進制類型  this.ws.onopen    = () => this.handleOpen();                      // 連接成功:contentReference[oaicite:0]{index=0}  this.ws.onmessage = e  => this.handleMessage(e);                  // 收到消息  this.ws.onclose   = e  => this.handleClose(e);                    // 連接關閉  this.ws.onerror   = e  => this.handleError(e);                    // 錯誤處理  
}

2. 實現心跳機制

2.1 為什么要心跳

許多網絡設備會在連接空閑一定時長后斷開,心跳可保持連接活躍并及時發現斷線MDN Web Docs。

2.2 心跳代碼

const DEFAULTS = { HEART_INTERVAL: 10000, STATUS_CHECK: 3000 };handleOpen() {this.clearTimers();this.startHeartbeat();
}startHeartbeat() {this.heartbeatTimer = setInterval(() => {if (this.ws.readyState === WebSocket.OPEN) {this.ws.send(JSON.stringify({ type: 'PING' }));             // 心跳包  }}, DEFAULTS.HEART_INTERVAL);this.statusCheckTimer = setInterval(() => {if (this.ws.readyState !== WebSocket.OPEN) {this.reconnect();                                            // 狀態檢查:contentReference[oaicite:3]{index=3}  }}, DEFAULTS.STATUS_CHECK);
}clearTimers() {clearInterval(this.heartbeatTimer);clearInterval(this.statusCheckTimer);
}

3. 自動重連策略

3.1 基本重連

在斷線或錯誤時,調用 reconnect()

reconnect() {if (this.reconnecting) return;this.reconnecting = true;this.clearTimers();setTimeout(() => {this.reconnectCount++;if (this.reconnectCount <= 5) {                                // 最多重連5次  this.connect();} else {this.onDisconnect();}this.reconnecting = false;}, 2000);                                                       // 延遲2秒重連:contentReference[oaicite:5]{index=5}
}handleClose(e) {console.warn('WebSocket closed:', e);this.reconnect();
}handleError(e) {console.error('WebSocket error:', e);this.reconnect();
}

4. 消息接收與分發

handleMessage 中解析并交給用戶回調:

handleMessage(event) {try {const data = event.data instanceof ArrayBuffer? JSON.parse(new TextDecoder().decode(event.data)): JSON.parse(event.data);this.onMessage(data);} catch (err) {console.error('Message parse error:', err);}
}
  • 兼容二進制/文本:用 TextDecoder 解碼二進制MDN Web Docs。

  • 異常捕獲:防止單條消息解析失敗導致整個連接中斷Stack Overflow。


5. 完整示例代碼

class WSocket {constructor({ url, onMessage, onDisconnect }) {this.url = url;this.onMessage = onMessage;this.onDisconnect = onDisconnect;this.ws = null;this.heartbeatTimer = null;this.statusCheckTimer = null;this.reconnecting = false;this.reconnectCount = 0;}connect() {this.ws = new WebSocket(this.url);this.ws.binaryType = 'arraybuffer';this.ws.onopen    = () => this.handleOpen();this.ws.onmessage = e => this.handleMessage(e);this.ws.onclose   = e => this.handleClose(e);this.ws.onerror   = e => this.handleError(e);}handleOpen() {this.reconnectCount = 0;this.clearTimers();this.startHeartbeat();console.log('WebSocket connected');}startHeartbeat() {this.heartbeatTimer = setInterval(() => {if (this.ws.readyState === WebSocket.OPEN) {this.ws.send(JSON.stringify({ type: 'PING' }));}}, DEFAULTS.HEART_INTERVAL);this.statusCheckTimer = setInterval(() => {if (this.ws.readyState !== WebSocket.OPEN) this.reconnect();}, DEFAULTS.STATUS_CHECK);}clearTimers() {clearInterval(this.heartbeatTimer);clearInterval(this.statusCheckTimer);}reconnect() {if (this.reconnecting) return;this.reconnecting = true;this.clearTimers();setTimeout(() => {this.reconnectCount++;if (this.reconnectCount <= 5) {this.connect();} else {this.onDisconnect();}this.reconnecting = false;}, 2000);}handleMessage(event) {try {const raw = event.data instanceof ArrayBuffer? new TextDecoder().decode(event.data): event.data;const data = JSON.parse(raw);this.onMessage(data);} catch (err) {console.error('Message parse error:', err);}}handleClose(e) {console.warn('WebSocket closed:', e);this.reconnect();}handleError(e) {console.error('WebSocket error:', e);this.reconnect();}send(data) {if (this.ws?.readyState === WebSocket.OPEN) {this.ws.send(typeof data === 'string' ? data : JSON.stringify(data));}}disconnect() {this.clearTimers();this.ws?.close();}
}

6. 使用示例

const ws = new WSocket({url: 'wss://example.com/live',onMessage: msg => console.log('Recv:', msg),onDisconnect: () => alert('Connection lost')
});ws.connect();// 發送消息
ws.send({ type: 'CHAT', content: 'Hello world' });// 手動斷開
// ws.disconnect();

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/83256.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/83256.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/83256.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

UEFI Spec 學習筆記---33 - Human Interface Infrastructure Overview(1)

33 - Human Interface Infrastructure Overview 本章節主要用于介紹Human Interface Infrastructure&#xff08;HII&#xff09;架構介紹&#xff0c;描述如何通過 HII 來管理用戶的輸入&#xff0c;以及描述在 UEFI spec 中涉及 HII 相關的 Protocol、function 和類型定義。…

ip命令詳解

控制網卡的硬件狀態 ip link set ens36 down ip link set ens36 up 修改網卡名稱&#xff08;臨時&#xff09; ip link set ens36 down ip link set ens36 name xxx 修改網卡的mac地址 ip link set ens36 down ip link set xxx name ens36 查看ip的addr ip addr show ip ad…

hadoop中了解yarm

Hadoop中的YARN&#xff08;Yet Another Resource Negotiator&#xff09;是一種新的Hadoop資源管理器&#xff0c;是一個通用資源管理系統&#xff0c;可為上層應用提供統一的資源管理和調度。以下是其相關介紹&#xff1a; 核心思想 將JobTracker的資源管理和作業調度/監控功…

做好的QT軟件,換一個筆記本打開后發現字體很小,部分字體還被控件遮擋

出現這種情況的原因主要是屏幕的DPI&#xff08;每英寸點數&#xff09;不同。Qt中控件的大小單位為像素&#xff0c;在高DPI下&#xff0c;控件會變小&#xff0c;低DPI下控件會變大。而Qt中字體的單位默認為磅&#xff0c;無論在什么顯示器上顯示同一磅值的字體&#xff0c;其…

linux - 權限的概念

目錄 用戶權限 超級用戶與普通用戶的區別 超級用戶&#xff08;root&#xff09;&#xff1a; 普通用戶&#xff1a; 切換用戶身份 使用sudo執行高權限命令 用戶管理 用戶組管理 文件權限 文件訪問者類別 基本權限 權限表示方法 權限修改 chmod chown chgrp u…

Python函數返回值的藝術:為何True/False是更優實踐及例外情況分析

在Python編程實踐中&#xff0c;子程序的返回值設計往往是一個容易被忽視但卻至關重要的設計決策。本文將深入探討為什么返回True/False往往是更好的選擇&#xff0c;何時應該避免這種做法&#xff0c;以及如何處理與None值相關的問題。 為什么返回True/False是更好的實踐&…

STM32單片機內存分配詳細講解

單片機的內存無非就兩種&#xff0c;內部FLASH和SRAM&#xff0c;最多再加上一個外部的FLASH拓展。在這里我以STM32F103C8T6為例子講解FLASH和SRAM。 STM32F103C8T6具有64KB的閃存和20KB的SRAM。 一. Flash 1.1 定義 非易失性存儲器&#xff0c;即使在斷電后&#xff0c;其所…

【Tools】Visual Studio使用經驗介紹(包括基本功能、遠程調試、引入第三方庫等等)

這里寫目錄標題 1. VS基本使用1.1. 快捷鍵1.2. 查看變量地址1.3. 查看代碼匯編1.4. visual studio 熱重載功能的使用1.5. vs遠程服務器調試1.6. 引入第三方庫VLD1.7. release debug模式 1. VS基本使用 1.1. 快捷鍵 ctrl c :復制光標所在行 注意&#xff1a;只需要光標在這…

網絡爬蟲學習之httpx的使用

開篇 本文整理自《Python3 網絡爬蟲實戰》&#xff0c;主要是httpx的使用。 筆記整理 使用urllib庫requests庫的使用&#xff0c;已經可以爬取絕大多數網站的數據&#xff0c;但對于某些網站依然無能為力。 這是因為這些網站強制使用HTTP/2.0協議訪問&#xff0c;這時urllib和r…

Python內存管理:賦值、淺拷貝與深拷貝解析

賦值與共享資源 在Python中&#xff0c;直接賦值操作&#xff08;如 list2 list1&#xff09;會導致兩個變量共享同一個內存地址。這意味著對 list1 的修改會直接影響到 list2&#xff0c;因為它們指向同一個對象。 注意: 賦值等于完全共享資源 如果我們不希望這樣完全共享&…

CentOS7原有磁盤擴容實戰記錄(LVM非LVM)【針對GPT分區】

一、環境 二、命令及含義 fdisk ????fdisk?是一個較老的分區表創建和管理工具&#xff0c;主要支持MBR&#xff08;Master Boot Record&#xff09;格式的分區表。MBR分區表支持的硬盤單個分區最大容量為2TB&#xff0c;最多可以有4個主分區。fdisk通過命令行界面進行操…

獲取相機圖像(ROS2)

文章目錄 前言一、獲取筆記本自帶相機圖像1.打開終端2.安裝usb-cam功能包3.啟動相機節點4.再打開一個終端5.啟動rqt查看圖像(1)方法一&#xff1a;點擊窗口選項&#xff0c;打開圖像話題(2)方法二&#xff1a;使用命令行&#xff0c;直接打開圖像話題 二、獲取USB相機圖像總結 …

Go 語言中接口類型轉換為具體類型

類型轉換方法 在 Go 語言中&#xff0c;將接口類型轉換為具體類型主要有以下幾種方法&#xff1a; 1. 類型斷言&#xff08;Type Assertion&#xff09; var i interface{} "hello"// 基本形式 s : i.(string) // 將接口i轉換為string類型 fmt.Println(s) // 輸…

ES C++客戶端安裝及使用

介紹 Elasticsearch &#xff0c; 簡稱 ES &#xff0c;它是個開源分布式搜索引擎&#xff0c;它的特點有&#xff1a;分布式&#xff0c;零配置&#xff0c;自動發現&#xff0c;索引自動分片&#xff0c;索引副本機制&#xff0c;restful 風格接口&#xff0c;多數據源&…

力扣-94.二叉樹的中序遍歷

題目描述 給定一個二叉樹的根節點 root &#xff0c;返回 它的 中序 遍歷 。 class Solution { public:void inorder(TreeNode* root, vector<int>& res){//C這里&一定要加if(!root)return;inorder(root->left,res);res.push_back(root->val);inorder(ro…

《大模型微調實戰:Llama 3.0全參數優化指南》

全參數微調&#xff08;Full Parameter Fine-Tuning&#xff09;是推動大模型適應垂直領域任務的核心技術&#xff0c;尤其對于Llama 3.0這類千億級參數模型而言&#xff0c;其性能優化與場景適配能力直接決定了實際應用價值。然而&#xff0c;全參數微調面臨計算成本高、內存占…

張 提示詞優化(相似計算模式)深度學習中的損失函數優化技巧

失函數的解釋 損失函數代碼解析 loss = -F.log_softmax(logits[

《Spring Boot 4.0新特性深度解析》

Spring Boot 4.0的發布標志著Java生態向云原生與開發效能革命的全面邁進。作為企業級應用開發的事實標準框架&#xff0c;此次升級在運行時性能、云原生支持、開發者體驗及生態兼容性四大維度實現突破性創新。本文深度解析其核心技術特性&#xff0c;涵蓋GraalVM原生鏡像支持、…

協作賦能-1-制造業生產流程重構

制造業生產流程重構——從“信息孤島”到“全鏈協同” 在制造業的數字化轉型浪潮中&#xff0c;一個看似矛盾的現象正在蔓延&#xff1a;企業部署了ERP、MES、PLM等管理系統&#xff0c;卻仍未擺脫“紙質工單滿天飛、跨部門扯皮不斷”的困境。以汽車制造業為例&#xff0c;其…

基于React的高德地圖api教程002:自定義地圖樣式

文章目錄 2、自定義地圖樣式2.1 自定義底圖樣式2.2 添加衛星地圖和路網圖2.3 完整代碼下載2、自定義地圖樣式 2.1 自定義底圖樣式 高德地圖提供了多種地圖樣式,對底圖進行設置,可選樣式如下圖所示: 添加地圖樣式切換控件: <div style={{marg