前端視角下關于 WebSocket 的簡單理解

參考 RFC 6455: The WebSocket Protocol

WebSocket 協議基礎

  • 協議本質:在單個 TCP 連接上提供全雙工通信通道的協議
  • 核心優勢:
    • 雙向實時通信(服務器主動推送)
    • 低延遲(相比 HTTP 輪詢)
    • 高效數據傳輸(減少 HTTP 頭部開銷)
  • 協議握手:
# 來自客戶端的握手數據
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
- Connection:設置 Upgrade,表示客戶端希望連接升級
- Upgrade:設置 websocket,表示希望升級到 Websocket 協議
- Sec-WebSocket-Key:客戶端發送的一個 base64 編碼的密文,用于簡單的認證秘鑰。要求服務端必須返回一個對應加密的 Sec-WebSocket-Accept 應答,否則客戶端會拋出錯誤,并關閉連接
- Sec-WebSocket-Protocol:子協議選擇, 標識客戶端支持的協議
- Sec-WebSocket-Version :表示支持的 Websocket 版本
- Sec-WebSocket-Extensions:戶端期望使用的協議級別的擴展
# 服務端的握手響應
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
- HTTP/1.1 101 Switching Protocols:表示服務端接受 WebSocket 協議的客戶端連接
- Sec-WebSocket-Accept:驗證客戶端請求報文,同樣也是為了防止誤連接。具體做法是把請求頭里 Sec-WebSocket-Key 的值,加上一個專用的 UUID,再計算摘要
  • 結束握手:任何一端都可以發送一個包含特定關閉握手的控制幀數據。收到此幀后,另一端在不發送任何數據后會發送一個結束幀作為響應。收到另一端的結束幀后,最開始發送控制幀的端在沒有數據需要發送時,就會安全的關閉此連接。在發送了一個表明連接需要被關閉的控制幀后,這個客戶端不會再發送任何的數據;在收到一個表明連接需要被關閉的控制幀后,這個客戶端會丟棄此后的所有數據。

WebSocket 幀結構

在 WebSocket 協議中,數據是通過一系列數據幀來進行傳輸的。為了避免安全問題,客戶端必須在它發送到服務器的所有幀中添加掩碼(Mask),服務端收到沒有添加掩碼的數據幀以后,必須立即關閉連接。另外服務端禁止在發送數據幀給客戶端時添加掩碼,客戶端如果收到了一個添加了掩碼的幀,必須立即關閉連接。

      0                   1                   2                   30 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1+-+-+-+-+-------+-+-------------+-------------------------------+|F|R|R|R| opcode|M| Payload len |    Extended payload length    ||I|S|S|S|  (4)  |A|     (7)     |             (16/64)           ||N|V|V|V|       |S|             |   (if payload len==126/127)   || |1|2|3|       |K|             |                               |+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +|     Extended payload length continued, if payload len == 127  |+ - - - - - - - - - - - - - - - +-------------------------------+|                               |Masking-key, if MASK set to 1  |+-------------------------------+-------------------------------+| Masking-key (continued)       |          Payload Data         |+-------------------------------- - - - - - - - - - - - - - - - +:                     Payload Data continued ...                :+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +|                     Payload Data continued ...                |+---------------------------------------------------------------+

基礎幀頭

位偏移字段名稱長度詳細說明
0FIN1 bit標識是否為消息的最后一幀(1=最終幀,0=還有后續幀)
1-3RSV1, RSV2, RSV3各1 bit保留位,必須為0(除非擴展協議定義特殊用途)
4-7Opcode4 bits幀類型標識: 0x0=連續幀;0x1=文本幀;0x2=二進制幀;0x8=關閉幀; 0x9=PING;0xA=PONG
8Mask1 bit是否使用掩碼(客戶端到服務器必須設為1)
9-15Payload Length7 bits數據長度(實際值分三種情況): 0-125:實際長度;126:后續2字節表示長度;127:后續8字節表示長度

Opcode類型表

Hex類型描述
0x0Continuation連續幀(分片消息)
0x1TextUTF-8文本數據
0x2Binary二進制數據
0x8Close連接關閉指令
0x9Ping心跳檢測請求
0xAPong心跳檢測響應

長度解碼規則

Payload Length值后續字節數實際長度范圍
0-12500-125字節
1262126-65,535字節
127865,536-2^64-1字節

控制幀特殊說明

所有的控制幀必須有一個 126 字節或者更小的負載長度,并且不能被分片

  1. Close幀(0x8):

    • 前2字節:狀態碼(如1000表示正常關閉)
    • 可選 UTF-8 原因短語
  2. Ping/Pong 幀(0x9/0xA):

    • 必須實現心跳應答機制
    • Pong 的 Payload 需與對應 Ping 一致

示例幀解析

Hello 文本幀原始字節(Hex):

81 85 37 FA 21 3D 7F 9F 4D 51 58

解析結果:

  • 81 → FIN=1, Opcode=0x1(文本幀)
  • 85 → Mask=1, Payload Length=5
  • 37 FA 21 3D → 掩碼密鑰
  • 7F 9F 4D 51 58 → 加密后的 “Hello”

前端 WebSocket 高可用框架設計

暫時沒有設計日志和統計系統,項目地址 websocket-pro-client

組件關系圖

WebSocketManager
WebSocketClient
TaskScheduler
EventEmitter
Heartbeat
PriorityQueue
  • WebSocketManager:入口類,管理所有連接實例和共享資源
  • WebSocketClient:單個連接實例,處理連接生命周期和消息收發
  • Heartbeat:心跳檢測管理,維護連接活性
  • TaskScheduler:任務調度器,控制并發消息發送
  • PriorityQueue:優先級隊列,確保高優先級消息優先處理
  • EventEmitter:事件中心,統一處理所有連接事件

分層架構設計

外部服務
網絡層
核心層
用戶層
事件訂閱
API調用
WebSocket Server
Browser WebSocket
Connection Pool
WebSocketClient1
WebSocketClient2
Heartbeat
Task Scheduler
PriorityQueue
EventEmitter
UI Components
WebSocketManager

架構說明

  1. 用戶層

    • UI Components:業務組件,通過標準API與核心層交互
    • 事件流:通過EventEmitter實現松耦合通信
  2. 核心層

    • WebSocketManager:單例入口
    • Connection Pool:連接池維護策略:
      • 最大連接數限制(默認5個)
      • LRU(最近最少使用)淘汰機制
      • 相同URL自動復用連接
  3. 網絡層

    • 封裝原生WebSocket API,增加:
      • 自動重連裝飾器
      • 二進制數據分片處理
      • CORS安全校驗

數據流轉過程

用戶界面WebSocketManagerWebSocketClient網絡層TaskSchedulerconnect('wss://api.example.com')創建新連接初始化WebSocketonopen啟動心跳檢測連接狀態更新'connected'事件發送PING返回PONGloop[心跳檢測]send(data, priority)添加發送任務有序發送數據onerror觸發重連流程alt[網絡異常]用戶界面WebSocketManagerWebSocketClient網絡層TaskScheduler

詳細設計說明

連接管理

連接狀態機:

connect()
onopen
onerror/onclose
close()
onclose
onerror/onclose
disconnected
connecting
connected
closing

連接池實現:

class WebSocketManager {private connectionPool: Map<string, WebSocketClient>;// 獲取或創建連接public connect(url: string): WebSocketClient {if (this.connectionPool.has(url)) {return this.connectionPool.get(url)!; // 復用現有連接}const client = new WebSocketClient(url, this.config);this.connectionPool.set(url, client);return client;}// 關閉所有連接public closeAll(): void {this.connectionPool.forEach(client => client.close());}
}

連接池 vs 單連接

  • 優點:避免重復握手開銷,支持多租戶隔離
  • 缺點:增加內存占用,需要維護狀態一致性

錯誤處理體系

錯誤類型處理方式
連接錯誤自動觸發重連機制,累計重試次數
心跳超時主動關閉連接并標記為異常斷開,觸發快速重連
消息發送失敗根據優先級存入隊列,連接恢復后自動重發
協議錯誤關閉連接并觸發error事件,不自動重連

錯誤捕獲示例

// 網絡層錯誤捕獲
socket.addEventListener('error', (event) => {this.status = 'disconnected';this.emit('error', {type: 'network',error: event,willReconnect: this.reconnectAttempts < this.config.maxReconnectAttempts});this.scheduleReconnect();
});// 應用層錯誤處理
public send(data: any): Promise<void> {return new Promise((resolve, reject) => {if (this.status !== 'connected') {reject(new Error('Connection not ready'));return;}try {this.socket.send(data);resolve();} catch (error) {this.emit('error', {type: 'send',error,data});reject(error);}});
}

智能重連機制

重連算法流程:

ClientManager連接斷開計算退避時間嘗試重連連接恢復增加重試計數重新計算等待時間alt[成功][失敗]loop[重試邏輯]ClientManager

核心代碼:

private scheduleReconnect(): void {// 1. 檢查重試上限if (this.reconnectAttempts >= this.config.maxReconnectAttempts) {this.emit('reconnect_failed', {attempts: this.reconnectAttempts,maxAttempts: this.config.maxReconnectAttempts});return;}// 2. 指數退避計算const baseDelay = this.config.reconnectDelay;const exponent = Math.pow(this.config.reconnectExponent, this.reconnectAttempts);const cappedDelay = Math.min(baseDelay * exponent,this.config.maxReconnectDelay);// 3. 添加隨機抖動(避免集群同時重連的"驚群效應")const jitterRatio = 0.2; // ±20%的隨機波動const jitter = cappedDelay * jitterRatio * (Math.random() * 2 - 1); // [-0.2,0.2]范圍const actualDelay = Math.max(1000, cappedDelay + jitter); // 保證至少1秒// 4. 設置定時器this.reconnectTimer = setTimeout(() => {this.reconnectAttempts++;this.emit('reconnect', {attempt: this.reconnectAttempts,nextDelay: actualDelay});// 5. 實際重連操作this.connect()}, actualDelay);
}

指數退避公式
每次重試間隔 = min(初始延遲 * (退避系數^重試次數), 最大延遲)

# 初始值
initialDelay = 1000ms, 
exponent = 1.5, 
maxDelay = 30000ms# 重試間隔增長示例:
第1次: 1000ms
第2次: 1500ms (1000*1.5^1)
第3次: 2250ms (1000*1.5^2)
...
第10次: 30000ms (達到上限)

另外可添加服務端過載保護:

if (this.reconnectAttempts > 3) {// 隨機跳過1次重試if (Math.random() < 0.3) {this.reconnectAttempts++;this.scheduleReconnect();return;}
}

心跳檢測系統

ClientServerPING收到PING后立即響應PONG啟動超時計時器重置計時器標記連接異常主動關閉alt[正常響應][超時未響應]ClientServer

核心代碼:

class Heartbeat {private lastPong: number = 0;public start(): void {this.intervalId = setInterval(() => {if (this.socket.readyState === WebSocket.OPEN) {this.socket.send('ping');this.timeoutId = setTimeout(() => {this.onTimeout(); // 心跳超時處理}, this.timeout);}}, this.interval);}public recordPong(): void {this.lastPong = Date.now();clearTimeout(this.timeoutId);this.emit('latency', Date.now() - this.lastPong);}
}

消息調度系統

優先級隊列設計:

優先級消息類型默認權重
0系統控制消息(如心跳)最高
1用戶關鍵操作
2普通數據更新
3批量日志/非實時數據

優先級調度策略:

  • 嚴格優先級,適用于金融交易系統等
  • 加權輪詢,適用于物聯網數據采集等
  • 動態調整,適用于視頻流傳輸等

調度器核心代碼:

interface Task {task: () => Promise<void>;priority: number;
}class PriorityQueue {private items: Task[] = [];public enqueue(task: Task): void {let added = false;for (let i = 0; i < this.items.length; i++) {if (task.priority > this.items[i].priority) {this.items.splice(i, 0, task);added = true;break;}}if (!added) {this.items.push(task);}}public dequeue(): Task | undefined {return this.items.shift();}public get length(): number {return this.items.length;}
}
class TaskScheduler {public addTask(task: () => Promise<void>, priority: number): Promise<void> {return new Promise((resolve, reject) => {this.queue.enqueue({task: async () => {try {await task();resolve();} catch (error) {reject(error);}},priority});this.run();});}private run(): void {while (this.runningTasks < this.maxConcurrent && this.queue.length > 0) {const { task } = this.queue.dequeue()!;this.runningTasks++;task().finally(() => {this.runningTasks--;this.run(); // 遞歸執行下一個任務});}}
}

性能優化策略

  • 使用ArrayBuffer傳輸圖像數據
  • 消息壓縮
  • 帶寬自適應(自動適應從2G到5G的網絡環境,基于網絡類型調整策略,如心跳間隔,最大連接數等)

總結

WebSocket 是一種網絡傳輸協議,位于 OSI 模型的應用層。可在單個 TCP 連接上進行全雙工通信,能更好的節省服務器資源和帶寬并達到實時通迅。客戶端和服務器只需要完成一次握手,兩者之間就可以創建持久性的連接,并進行雙向數據傳輸。

特點

  • 全雙工,通信允許數據在兩個方向上同時傳輸,它在能力上相當于兩個單工通信方式的結合
  • 二進制幀,采用了二進制幀結構,語法、語義與 HTTP 完全不兼容,相比 http/2,WebSocket 更側重于“實時通信”,而 http/2 更側重于提高傳輸效率,所以兩者的幀結構也有很大的區別。不像 http/2 那樣定義流,也就不存在多路復用、優先級等特性,自身就是全雙工,也不需要服務器推送
  • 協議名,引入 wswss 分別代表明文和密文的 WebSocket 協議,且默認端口使用 80 或 443,幾乎與 http 一致
  • 握手,WebSocket 也要有一個握手過程,然后才能正式收發數據。

優點

  • 較少的控制開銷:數據包頭部協議較小,不同于 http 每次請求需要攜帶完整的頭部
  • 更強的實時性:相對于 http 請求需要等待客戶端發起請求服務端才能響應,延遲明顯更少
  • 保持創連接狀態:創建通信后,可省略狀態信息,不同于 http 每次請求需要攜帶身份驗證
  • 更好的二進制支持:定義了二進制幀,更好處理二進制內容
  • 支持擴展:用戶可以擴展 WebSocket 協議、實現部分自定義的子協議
  • 更好的壓縮效果:WebSocket 在適當的擴展支持下,可以沿用之前內容的上下文,在傳遞類似的數據時,可以顯著地提高壓縮率

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

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

相關文章

自動化一鍵部署 LNMP 環境

第一步&#xff1a;準備環境 & 準備腳本文件1. 你在 CentOS 7 的服務器/虛擬機里打開終端&#xff0c;確認你有 root 權限或者能用 sudo。輸入下面命令確認你的系統版本&#xff1a;cat /etc/centos-release你應該看到類似&#xff1a;CentOS Linux release 7.9.2009 (Core…

react之React.cloneElement()

react提供的這個方法克隆組件的方法&#xff0c;可能我們在平常的開發中用的很少&#xff0c;主要可能是我們并不知道或者并不了解這個方法。因為我在之前react的children文章中用到過&#xff0c;所以我就進行了一系列的測試&#xff0c;發現真的非常的好用。我們同樣使用一些…

學習Java的Day27

今天學習的主要內容是在IntelliJ IDEA開發環境中&#xff0c;通過部署Tomcat服務器并連接MySQL數據庫&#xff0c;實現了一個完整的留言板系統。這個項目涵蓋了前后端開發的全流程&#xff0c;具體包括以下關鍵環節&#xff1a;開發環境搭建使用IntelliJ IDEA Ultimate版&#…

【計算機網絡 | 第3篇】物理媒介

文章目錄物理媒介介紹與物理媒體的分類&#x1f95d;成本考量引導型傳輸媒體&#x1f34b;引導型傳輸媒體&#xff1a;雙絞線&#x1f34b;?&#x1f7e9;雙絞線類別雙絞線的發展歷程雙絞線的物理限制引導型傳輸媒體&#xff1a;同軸電纜&#x1f34b;?&#x1f7e9;結構組成…

golang的切片

切片 為什么需要切片 用于元素的個數不確定&#xff0c;所以無法通過數組的形式來進行統計。此時就需要切片 切片&#xff0c;也因此可以粗略地理解為動態數組數組的長度不能用變量來確定&#xff0c;這時候切片slice也就派上用場了 切片地基本介紹 切片的英文是slice切片是數組…

在labview中實現視頻播放

這里分享一個迅雷的視頻播放控件APlayer&#xff0c;非常的好用。具體操作步驟如下&#xff1a; 1.下載控件: 首先下載http://aplayer.open.xunlei.com/codecs.zip&#xff0c;將codecs文件解壓后打開&#xff0c;按快捷鍵contrlA,隨后contrlc復制里面所有的文件&#xff1b;…

ubuntu 22.04 使用yaml文件 修改靜態ip

前提&#xff1a; 啟動服務 sudo systemctl start systemd-networkd 設置開機自啟 sudo systemctl enable systemd-networkd 檢查狀態&#xff08;確保顯示 active (running)&#xff09; sudo systemctl status systemd-networkd 若想停止&#xff1a; 停止當前運行的服務 sud…

閘機控制系統從設計到實現全解析:第 4 篇:Redis 緩存與分布式鎖實現

第 4 篇&#xff1a;Redis 緩存與分布式鎖實現 一、Redis 在系統中的核心作用票證信息緩存&#xff1a;將高頻訪問的票證數據&#xff08;如狀態、有效期&#xff09;緩存至 Redis&#xff0c;減少數據庫查詢&#xff0c;提升驗證響應速度。分布式鎖&#xff1a;在高并發場景下…

北京天津唐山廊坊滄州打撈日記

北京天津唐山廊坊滄州打撈日記 打撈搜蚯蚓疏通 北京&#xff1a;護城河畔的情誼打撈 清晨&#xff0c;北京的護城河在朝陽的映照下泛著微光。我接到一位年輕小伙的電話&#xff0c;聲音中滿是焦急。原來&#xff0c;他與女友在河邊約會時&#xff0c;不小心將女友送他的定情玉佩…

全志刷機工具:PhoenixSuit-全志芯片處理器-刷機工具安裝包及最詳細使用教程指南

全志刷機工具&#xff1a;PhoenixSuit-全志芯片處理器刷機工具安裝包及最詳細使用教程指南&#xff0c;此文章主要是分享機頂盒、電視盒子&#xff0c;全志芯片盒子&#xff08;其中包含全志處理器、全志芯片、全志CPU等等&#xff09;的刷機工具、刷機工具安裝教程以及如何使用…

淺談 VM 橋接模式:讓虛擬機像真實電腦一樣接入網絡

在虛擬化環境中&#xff0c;虛擬機&#xff08;Virtual Machine, VM&#xff09;與外部網絡之間的通信方式有多種&#xff0c;比如 NAT 模式、Host-Only 模式、橋接模式&#xff08;Bridged Networking&#xff09; 等。其中&#xff0c;橋接模式是最接近“真實物理機”網絡行為…

計算機視覺(1)-圖像采集設備選型全景表(工業 + 醫療 + 車載)

圖像采集設備選型全景表&#xff08;工業 醫療 車載&#xff09;一份面向工程師的“場景—設備—協議”速查表1 工業 & 醫療 & 通用場景應用場景主流設備形態接口 / 協議典型性能突出優勢致命短板動態范圍工業檢測AOI / 量測 / 缺陷工業相機 采集卡Camera Link HSCo…

計算機視覺(3)深度學習模型部署平臺技術選型與全棧實踐指南

一、部署平臺概述與分類 深度學習模型部署平臺的分類需兼顧技術特性與應用場景的適配性&#xff0c;基于“技術定位-場景適配”雙維度分類法&#xff0c;可將其劃分為通用開源框架、云廠商服務及專用邊緣工具三大類&#xff0c;各類別在設計目標、核心能力與場景覆蓋上呈現顯著…

Scratch編程:槍戰游戲(附源碼)

&#x1f3ae; 操作說明 W / A / S / D 或 方向鍵&#xff1a;移動 C&#xff1a;滑鏟 空格鍵&#xff1a;取消滑鏟 鼠標點擊&#xff1a;開火 數字鍵 1 / 2 / 3 / 4&#xff1a;切換武器 G&#xff1a;快速使用道具 F&#xff1a;近戰攻擊 Q&#xff1a;瞄準 / 使用技能…

應急響應復現

一、前言&#xff1a;當企業發生黑客入侵、系統崩潰或其它影響業務正常運行的安全事件時&#xff0c;急需第一時間進行處理&#xff0c;使企業的網絡信息系統在最短時間內恢復正常工作&#xff0c;進一步查找入侵來源&#xff0c;還原入侵事故過程&#xff0c;同時給出解決方案…

分布式事務Seata TCC模式篇

介紹 ? 官網: https://seata.apache.org/zh-cn/docs/user/mode/tcc ? 回顧Seata AT 模式基于 支持本地 ACID 事務 的 關系型數據庫&#xff0c;如下&#xff1a; 一階段 prepare 行為&#xff1a;在本地事務中&#xff0c;一并提交業務數據更新和相應回滾日志記錄。二階段 c…

Day37--動態規劃--52. 攜帶研究材料(卡碼網),518. 零錢兌換 II,377. 組合總和 Ⅳ,57. 爬樓梯(卡碼網)

Day37–動態規劃–52. 攜帶研究材料&#xff08;卡碼網&#xff09;&#xff0c;518. 零錢兌換 II&#xff0c;377. 組合總和 Ⅳ&#xff0c;57. 爬樓梯&#xff08;卡碼網&#xff09; 本文全部都是 ” 完全背包 “ 問題&#xff0c;從零到入坑&#xff0c;從入坑到爬出來。 本…

Linux文件操作

Linux文件Linux下的文件類型b 塊設備文件---->存儲類設備&#xff08;硬盤&#xff09;c 字符設備文件--->輸入輸出設備d 目錄文件--->文件夾- 普通文件--> xxx.c xxx.h xxx.txt xxx.jpg xxx.mp4 a.outl 軟鏈接文件-->快捷方式s 套接字文件-->網絡通信p 管道…

Linux epoll 觸發模式詳解:LT vs ET

兩種核心觸發模式 1. 水平觸發 (Level-Triggered, LT) 工作方式: 當文件描述符處于就緒狀態時,epoll 會持續通知 只要狀態未改變,每次調用 epoll_wait 都會返回該描述符 特點: c // 內核處理邏輯 (ep_send_events_proc) if (!(epi->event.events & EPOLLET)) { /…

STM32學習筆記6-TIM-2輸出比較功能

第二部分&#xff0c;定時器的輸出比較功能OC&#xff08;Output Compare&#xff09;輸出比較輸出比較可以通過比較CNT與CCR寄存器值的關系&#xff0c;來對輸出電平進行置1、置0或翻轉的操作&#xff0c;用于輸出一定頻率和占空比的PWM波形每個高級定時器和通用定時器都擁有4…