淺析WebSocket(1)

来源:https://www.cnblogs.com/vanter-sloe/archive/2022/06/27/16402131.html
-Advertisement-
Play Games

簡介 WebSocket 是雙工的,他支持在客戶端和伺服器之間互相發送文本或二進位消息流,除此功能以外,它還提供了更為複雜的附加擴展: 連接協商和同源策略實施 與現有HTTP基礎設施的互相操作性 面向消息的通信和高效的消息框架 這一點與Socket不同,Socket算是面向位元組,他沒有消息頭、消息尾 ...


簡介

WebSocket 是雙工的,他支持在客戶端和伺服器之間互相發送文本或二進位消息流,除此功能以外,它還提供了更為複雜的附加擴展:

  • 連接協商和同源策略實施

  • 與現有HTTP基礎設施的互相操作性

  • 面向消息的通信和高效的消息框架

    這一點與Socket不同,Socket算是面向位元組,他沒有消息頭、消息尾的概念。可以說Socket沒有那麼聰明

  • 子協議協商和可擴展性

值得註意的一點是:WebSocket 不是 HTTP、XHR 或 SSE 的替代品,為了獲得最佳性能,利用每種傳輸的優勢至關重要。

WS 和 WSS URL 方案

這兩種方案都是WebSocket的自定義方案,WS用於純文本(即:明文)通訊,WSS用於加密管道通訊。

文本消息和二進位消息

WebSocket比較好的一點就是:無需擔心緩衝、解析和重構接收到的數據。

例:如果伺服器發送 1 MB 的有效負載,客戶端每次只能接收250 kb的有效荷載,那麼他會接收四次,當接收完畢後onmessage才會在客戶端調用應用程式的回調。

有效負載:我有個長度為5 MB的位元組容器(即:一個int類型的數組),我要發送的消息轉為位元組數組後,長度只有1 MB,那麼這1 MB稱為有效負載,剩下的將是無效負載

有效載荷:客戶端每次只能接收250 kb(即:一個長度為250*1024的位元組數組),如果接收到了200 kb的數據,那麼200有效載荷 50無效載荷

性能

瀏覽器接收到一條新消息時,它會自動轉換為基於文本數據的 DOMString 對象或二進位數據的 Blob 對象,作為客戶端性能提升和優化的唯一方法就是,告訴瀏覽器將收到的二進位數據轉換為ArrayBuffer 而不是Blob

var ws = new WebSocket('wss://example.com/socket'); 
ws.binaryType = "arraybuffer";

Blob:離線存在磁碟中或單獨存在記憶體中

Blob 對象表示不可變的原始數據的類文件對象,如果您不需要改動二進位數據(例如我只需要這麼一個Blob文件對象),那麼這是最好的選擇。

ArrayBuffer:可能更有效將數據保存在記憶體中

如果您需要對二進位數據進行額外的處理,那麼ArrayBuffer可能更合適。

ArrayBuffer 是一個結構化的,二進位數據的固定長度容器

關於發送文本和二進位數據

WebSocket 連接成功後,客戶端將是一個雙向通訊管道,它允許通過同一個 TCP 連接在兩個方向上傳遞消息,發送或接收 UTF-8 和二進位消息。

預設Send方法,接受一個 DOMString 對象,該對象線上路上被編碼為 UTF-8,當然也可以用於二進位傳輸的 ArrayBuffer、ArrayBufferView 或 Blob 對象之一。

但是請註意,後一種二進位的方式只是為了方便API,在網路層面中,WebSocket的數據幀(即:發送的數據包),會通過單個位元組位標記為二進位或文本,因此,如果應用程式想要使用其他類型的信息,那麼雙方必須約定一種新的機制來通訊該數據。

關於Send方法

Send方法是非同步的,將提供的數據由客戶端排隊發出,立即返回結果,這裡的立即返回結果不代表你的信息已經發送完了,真正的發送完成是要監控當前瀏覽器的排隊數據量

var ws = new WebSocket('wss://example.com/socket');
ws.onopen = function () {
  // 當應用更新時觸發
  subscribeToApplicationUpdates(function(evt) {
    // 如果待發送的位元組數為0
    if (ws.bufferedAmount == 0)
      // 發送下一次請求
      ws.send(evt.data);
  });
};

所有 WebSocket 消息都按照它們在客戶端排隊的確切順序進行傳遞!!!

大量的排隊消息,或者單個大消息都將延遲其後面排隊的消息的傳遞——隊列頭阻塞!

解決方案:

  • 大消息拆分成更小的塊
  • 實現自己的優先順序隊列

應用程式應該密切關註每種類型的消息如何以及何時在套接字上排隊!

子協議協商

WebSocket 協議的預設消息格式只有兩種,文本數據和二進位數據,以便客戶端和服務端可以有效的對其進行編碼,如果不屬於這兩種,消息內容將是不透明的,服務端和客戶端將會不認識,導致無法解釋其內容。

WebSocket 與 HTTP 或 XHR請求不同,它們通過Header傳遞額外的信息,而WebSocket沒有這樣的協議,因此如果想要獲取額外的數據信息,那麼可以通過下麵的方式:

  • 統一的JSON編碼或自定義的二進位數據來通訊

  • 如果想要傳輸不同格式的數據,那麼可以通過約定消息頭

  • 文本和二進位消息混合使用

原始的WebSocket提供了子協議協商API來解決這個問題,最開始連接時,客戶端可以告訴伺服器他支持的協議,例:

var ws = new WebSocket('wss://example.com/socket', 
                       ['appProtocol', 'appProtocol-v2'])

如果子協議協商成功,則在 onopen處觸發回調,應用程式可以查詢WebSocket實例上的protocol屬性來確定伺服器選擇的協議,如果協商不成功,即伺服器不支持,則代表WebSocket協商是不完整的,將調用onerror回調。

WebSocket的協議

WebSocket協議由兩個高級組件組成:

  • 用於協商連接參數的開放 HTTP 握手
  • 一種二進位消息框架機制,允許文本和二進位數據的低開銷、基於消息的交付

WebSocket 協議是一個功能齊全的獨立協議,可以在瀏覽器之外使用。話雖如此,它的主要應用程式是作為基於瀏覽器的應用程式的雙向傳輸。

二進位框架層

WebSocket 使用了一種自定義的二進位幀格式,它將每個應用程式消息拆分為一個或多個,將它們傳輸到目的地,重新組裝它們,最後在收到整個消息後通知接收者。

  • 通信的最小單元,每一單元包含一個變長幀報頭和一個可以承載全部或部分應用程式消息的有效載荷。

  • 消息

    映射到邏輯應用程式消息的一個完整的幀序列。

我們瞭解一下WebSocket的是怎麼運行的?

我們先來看一下幀的內容:

幀是一個32位數據包(即:32-bit,一次處理4位元組,1位元組8位),我們來由淺入深一下:

比如a在ASCII編碼中代表著97,那麼97也就是所謂的位元組,然後再通過toString(2)得到01100001,這是一個8 bit(即:8位)的位數據,所謂的就是這樣的一堆二進位(也就是前面提到的8 bit)組成的。

現在我們來看一下伺服器發送給客戶端的原始數據,伺服器發送給客戶端的信息是兩個簡單的字元:aa

10000001 00000010 01100001 01100001

這是四位元組,你可以嘗試用後兩個位元組 01100001 01100001 去二進位轉換字元串(但是在轉換時您需要將它拼接起來,0110000101100001),你將得到信息aa

那麼前面兩個位元組中包含著什麼呢?

只是在目前的例子中他是占用了兩個位元組,其他情況下請參考數據內容

我們來看第一個位元組(10000001)

  • 1

    FIN:表示該數據包是不是消息的最後一個數據包(也就是說,如果他不是1,那麼表示數據包還沒有傳輸完成)

  • 000,分別代表RSV1、RSV2、RSV3,

    這三位必須是0,除非與伺服器協商了該擴展,如果這三個都不為0,那麼伺服器應該立即終止鏈接

  • 0001

    這四位代表操作碼

    • 0000:代表連續的幀
    • 0001:代表文本幀
    • 0010:代表二進位幀
    • 0011-0111:保留的幀,一般碰不到
    • 1000:代表連接要關閉
    • 1001:表示ping
    • 1010:表示pong
    • 1011-1111:保留的幀

我們接下來拆分第二個位元組(00000010)

  • 0

    掩碼,如果該位為1,那麼後面會有一個掩碼秘鑰,秘鑰占4位元組

  • 0000010

    這七位代表有效的載荷長度,轉換為數字為:2,也就是對應的aa的長度,在這裡如果轉換為的數字為0-125,那麼這就是應用數據的有效載荷長度。

    如果為126,那麼應該加上往後的擴展載荷長度,此情況下,擴展載荷長度為2個位元組。

    如果為127,那麼擴展載荷長度為8個位元組。

    如果有擴展的長度,那麼就需要將這些長度的位拼接起來,轉換為一個數字,該數字為真正的有效載荷的長度。

向後可能會出現的所屬於框架的位元組

  • 0或4位元組

    該項為掩碼秘鑰,用於屏蔽有效負載數據,即加密,此加密在本文下方有解釋。

  • 有效載荷數據:X+Y

    即擴展數據加應用數據

  • 擴展數據

    該數據應該是在創建連接(即:握手時)就應該協商好的,預設為0位元組,除非協商了擴展,以及定義好了擴展的長度,或長度應該如何計算。

  • 應用數據

    在擴展數據後的幀將是應用數據

掩碼、中介執行緩存中毒攻擊

掩碼是為了防止中介執行緩存中毒攻擊,該攻擊方式重點在於中介,此處的中介也就是一個代理伺服器,假設他是一個負載均衡代理,那麼他將負責轉發流量,我們知道WebSocket和HTTP並不相同,WebSocket發送到是原始位元組,這可以使其發送任何內容,比如模仿一個http請求:

soc.send(`
GET /script.js HTTP/1.1
HOST:惡意站點
`)

當這樣的一個消息,發送至中介伺服器時,中介伺服器並不知道這是一個WebSocket的數據封包,它將會被認為是HTTP請求,這將轉發到惡意站點,此時如果有相應的緩存設置,那麼接下來,訪問該安全站點 script.js 文件的無辜用戶,將會獲取到惡意站點的 script.js 文件。

而掩碼將會將 WebSocket 發送出去的數據進行屏蔽(加密),屏蔽完的數據他將不是明文的,而是看上去像是亂碼的數據。

至此,WebSocket的基本原理就是這樣,以上全文均為本人個人理解,如果有誤還請各位dai佬們指出。

接下來我會抽空將C#編寫的簡易WebSocketClient做成博客發出來。

第一次發博客,輕點噴,在此求求各位大佬了~


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Android系統日誌 我們經常需要查看設備裡面的各種日誌信息。所以有必要瞭解android系統裡面有哪些日誌,以及用什麼方式可以提取這些日誌。以我手頭的紅米note4xplus為例,其基本配置為高通msm8953,android7.0。我們可以看一下根文件系統: mido:/ # ls acct ...
  • 從全民娛樂到全民創作,音視頻、直播已成為文娛市場中最為活躍的內容形態,用戶在享受視聽娛樂的同時,也更期待通過這些平臺來表達自己。 面對用戶個性化需求的增加,影音娛樂應用開發者和內容平臺,該如何通過技術手段創新產品功能,提升用戶體驗,讓應用搶占用戶興趣“高地”? 6月28日,HDD-HMS Core. ...
  • 【layui2.6.8】有多個文件上傳的組件需要根據後臺數據在頁面載入時動態渲染在一個彈框裡面。彈窗從table表格數據的【編輯】按鈕打開,根據點擊行展示不同的文件,對文件進行查詢、刪除、下載等管理。問題:選擇文件,不上傳,關閉彈窗再打開,剛選的文件雖然不見了,但依然能上傳那個文件,需要打開彈窗時清 ...
  • 📰前言 這個小項目源於github項目:✨50 projects 50 days, 這個項目包含了50個小型前端項目,適合學習了Html+Css+JavaScript但是還沒有學習框架的前端新手作為練習。 這裡是原項目對模糊載入的代碼實現👉Blurry Loading. 📋分析 變化過程: 數 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 對vue項目來說,組件是構成項目的基本單元,為了方便理解,這裡定義兩類組件:頁面組件,功能組件。為什麼需要劃分這兩類組件是從組件復用來考慮的。 我們知道在複雜應用中,頁面狀態管理早已不是早期的簡單的子父組件的傳值,兄弟組件傳值。而當我們尋 ...
  • 自此整個項目前後臺,全部搭建完畢。 今天是最後一天,內容很多,而且也比較常用,一個圖標類數據可視化,一個後臺的許可權管理,都是很經典的類型。 一.數據可視化 1.簡介 專門的一門學科,有專門研究這個的崗位,將數據以各種圖形進行展示 Echarts只能2D,three.js可開發3D 2.canvas繪 ...
  • 什麼是單文件組件? 簡言之,單文件組件就是一個文件擴展名為.vue的single-file-components(SFC)。是Vue.js自定義的一種文件格式,一個.vue文件,就是一個單獨的組件,在文件內封裝了組件的相關代碼:HTML,CSS,JS。 瀏覽器本身並不支持.vue文件,所以必須對.v ...
  • 巨集任務和微任務的隊列入門知識,可以參考之前的文章: JavaScript的事件迴圈機制 巨集任務和微任務在前端面試中,被經常提及到,包括口頭和筆試題 async && await概念 async 使用async關鍵字聲明的函數,是AsyncFunction構造函數的實例,在async函數體內,可以使用 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...