WebSocket 深入:從 HTTP upgrade handshake 到 frame、masking 與 fragmentation
文字解析 · 1 支影片 · 產生於 2026-10-01 00:54
章節(8)
1. Outline
- 起點 · How Web Sockets work | Deep Dive
把 WebSocket 與 HTTP 的唯一交集(handshake)拆開,再往下看 frame 結構與 masking、fragmentation 兩個「為什麼」。
2. YouTuber 的思維推導
How Web Sockets work | Deep Dive
ByteMonk · 10m21s · 字幕 en · vision=on (input.yaml 指定 vision: true;作者多次說「headers looks like this」「format such as below」,標頭表與 frame 結構圖需要看畫面才能對照。) · YouTube
預估 vs 實際耗時
| 階段 | 預估 | 實際 |
|---|---|---|
| fetch | 6s | 2s |
| segment | 51s | 5m00s |
| shot | 22s | 4s |
| analyze | 3m45s | 6m48s |
| render | 5s | – |
作者的出發點是上一支基礎影片留下的問題:HTTP 每個 request 都要重新建連線、做完就關,伺服器負擔大;WebSocket 連線是持續的,但它到底怎麼從 HTTP 變出來?他先把兩者的關係定得很窄——WebSocket 是獨立的 TCP 協定,跟 HTTP 唯一的交集只有 handshake 會被 HTTP 伺服器讀成 upgrade request——於是整支影片的第一半就沿著這條 handshake 走:客戶端送什麼標頭(Upgrade、Connection、Sec-WebSocket-Key)、伺服器怎麼用 Key 加固定 GUID 做 SHA-1 再 base64 算出 Sec-WebSocket-Accept、客戶端怎麼驗證、哪些情況算失敗,最後 HTTP 連線被同一條 TCP 上的 WebSocket 取代。握手完成後資料怎麼走,他改用 frame 的結構回答:逐欄位介紹 FIN、RSV、opcode、Mask、payload length、masking key、payload,並刻意在 Mask 與 FIN 兩個欄位埋伏筆。
接著用「為什麼」把這兩個伏筆收回:masking 是為了讓 WebSocket 流量長得不像 HTTP,避免被舊式 caching proxy 誤快取;fragmentation 是為了避免大訊息撐爆 buffer、讓接收端可以邊收邊處理,而 FIN 位元就是分片的訊號。最後以聊天、即時更新、多人遊戲、協作畫布、股票報價等應用場景收尾,並把方法論說出來:拆開、實驗、理解每個功能背後的 why。
推理鏈:每段留給下一段的線索
每一列是一段,箭頭後的字是這段留給下一段的線索。點段落標題跳到該段。
- 為什麼要從 HTTP 升級成 WebSocket → 既然 WebSocket 與 HTTP 唯一的關係是 handshake 會被 HTTP 伺服器當成 upgrade request,那客戶端到底要送出什麼樣的 HTTP request、帶哪些標頭,伺服器才會認出「這是想升級成 WebSocket」?
- 客戶端發起 handshake:請求標頭 → 客戶端送出了帶隨機 Sec-WebSocket-Key 的 GET,現在正在等回應。伺服器收到後要檢查什麼、要拿這個 Key 做什麼,才能回一個讓客戶端相信「你真的懂 WebSocket」的答覆?
- 伺服器驗證並算出 Sec-WebSocket-Accept → 伺服器回了 101 加上算好的 Sec-WebSocket-Accept。客戶端收到後要怎麼驗?驗過之後這條連線會變成什麼樣子?如果 Accept 不對、標頭缺了、或狀態碼不是 101,又該怎麼辦?
- 握手完成、失敗條件與 URL 格式 → 連線已經升級成 WebSocket,雙方可以在同一條 TCP 上互送資料了。但作者說失敗時「不會送 frame」——所以 WebSocket 的資料到底是以什麼單位、什麼格式在這條連線上流動?frame 長什麼樣?
- WebSocket frame 的結構 → frame 的每個欄位都認得了,但作者留下兩個「為什麼」沒答:客戶端送出的 frame 為什麼一律要做 masking——遮罩用的 key 就放在同一個 frame 裡,這不是誰都能解嗎?以及 FIN 位元跟 fragment 到底怎麼配合?先看 masking。
- Masking:讓流量看起來不像 HTTP → masking 的「為什麼」收掉了。上一段還留了另一個:FIN 位元跟 fragment 怎麼配合——一則訊息什麼時候需要切成多個 frame、切了之後接收端怎麼知道收齊了?
- Fragmentation:大訊息分片與 FIN 位元 → handshake、frame、masking、fragmentation 都講完了,WebSocket 的機制已經完整。這些機制真正被用在哪些場景?作者最後給出應用清單並收尾。
- 應用場景與結語
3. 逐段說明
How Web Sockets work | Deep Dive
1. 為什麼要從 HTTP 升級成 WebSocket 0:00–1:06
作者回顧上一支基礎影片:HTTP 每個 request 都要另開連線與 handshake,做完就關,伺服器負擔大;WebSocket 連線則是持續的,除非任一方中斷。WebSocket 是獨立的 TCP 協定,和 HTTP 唯一的關係是它的 handshake 會被 HTTP 伺服器當成 upgrade request 解讀。
推理因為上一支影片已經建立「HTTP 每個 request 都要重新 handshake、做完就關,伺服器負擔大」這個痛點,所以作者接著先把 WebSocket 的定位說死:它不是 HTTP 的延伸,而是獨立的、以 TCP 為基礎的協定;兩者唯一的交集,是 WebSocket 的 handshake 會被 HTTP 伺服器讀成一個 upgrade request。這一句把整支影片的範圍框住——既然關係只在 handshake,那就先把 handshake 拆開看。
- CWebSocket 是獨立的 TCP 協定,不是 HTTP 的延伸;跟 HTTP 唯一的交集是 handshake 會被讀成 upgrade request→ 畫地圖
- RWebSocket 真正解的不是「每個 request 重開連線」,而是「伺服器不能主動推」→ 存+回想
AI 補充作者說「HTTP uses distinct connection for separate request」時,指的是最原始的 HTTP/1.0 行為;HTTP/1.1 其實有 keep-alive 可以重用 TCP 連線,但仍然是「客戶端問、伺服器答」的單向發起模式,伺服器不能主動推資料,這才是 WebSocket 真正要解決的問題。「伺服器要為每個 request 做一次 handshake」裡的 handshake 指 TCP 三向交握(若是 HTTPS 還要加 TLS 交握),跟本片主角「WebSocket handshake」是不同層的東西,後者是一次 HTTP 層的請求/回應。另外「持續、除非任一方中斷」的意思是連線一直開著,之後雙方隨時可以互送資料,不需要誰先問。
2. 客戶端發起 handshake:請求標頭 1:06–2:30
客戶端先開一條普通 HTTP 連線,URL 用 ws 或 wss 前綴,送出帶特定標頭的 GET request:Upgrade: websocket 表示想升級、Connection: Upgrade 表示要換掉目前協定、Sec-WebSocket-Key 是隨機產生的 base64 字串,之後伺服器會用它驗證。另可帶 Sec-WebSocket-Version、Sec-WebSocket-Protocol 等額外標頭,然後等伺服器回應。

推理因為上一段把 WebSocket 與 HTTP 的關係限縮在 handshake,所以作者接著把 handshake 拆成客戶端與伺服器兩側的步驟,先講客戶端。客戶端做的事很單純:開一條普通 HTTP 連線、URL 用 ws:// 或 wss://、送一個 GET,但要靠三個標頭表達「我要升級」——Upgrade: websocket 說明想換成哪個協定、Connection: Upgrade 說明這是連線層級的協定切換、Sec-WebSocket-Key 放一個隨機的 base64 字串給伺服器「之後驗證用」。作者刻意把 Key 的用途留白,只說「later」,然後讓客戶端等回應——問題自然移到伺服器那邊。
- CWebSocket handshake:一個帶三個關鍵標頭的 HTTP GET 加一個 101 回應,把這條連線從 HTTP 切成 WebSocket→ 畫地圖
- E畫面上完整的 upgrade GET 八行標頭:Host、Upgrade、Connection、Key、Origin、Protocol、Version→ 存+演練
- R為什麼要 Connection: Upgrade 和 Upgrade: websocket 兩個標頭而不是一個→ 存+回想
- RSec-WebSocket-Key 不是密碼、不是加密,只是 16 個隨機位元組的 base64(24 字元、== 結尾)→ 存+回想
- RSec-WebSocket-Version 固定 13;Sec-WebSocket-Protocol 是可選的子協定候選清單→ 存+回想
AI 補充畫面(107 秒)的完整 request 是:`GET /chat HTTP/1.1`、`Host: server.bytemonk.com`、`Upgrade: websocket`、`Connection: Upgrade`、`Sec-WebSocket-Key: dGhlIHJhbXBsZSBub25jZQ==`、`Origin: http://bytemonk.com`、`Sec-WebSocket-Protocol: chat, superchat`、`Sec-WebSocket-Version: 13`。作者沒說的兩點補上:(1) 為什麼要兩個標頭而不是一個——`Connection: Upgrade` 是 HTTP/1.1 的 hop-by-hop 標頭,告訴中間的 proxy「這條連線要換協定」;`Upgrade: websocket` 才指名換成什麼。少一個,伺服器都應拒絕。(2) `Sec-WebSocket-Key` 不是密碼、也不是加密用,它只是 16 個隨機位元組的 base64(固定 24 字元、`==` 結尾),目的是防止「不是 WebSocket 客戶端的程式」誤打誤撞建立連線;瀏覽器不允許 JavaScript 自行設定 `Sec-` 開頭的標頭,所以只有真正的 WebSocket API 能送出它。另外畫面上的 `Origin` 標頭作者沒提:瀏覽器一定會帶,伺服器可以用它擋掉來自其他網站的跨站 WebSocket 連線。`Sec-WebSocket-Version: 13` 是 RFC 6455 定案後唯一的版本號。
術語:WebSocket handshakews:// / wss://
3. 伺服器驗證並算出 Sec-WebSocket-Accept 2:30–4:05
伺服器收到 GET 後先確認 Upgrade 與 Connection 標頭都在,再拿 Sec-WebSocket-Key 接上一個固定的 GUID,做 SHA-1 hash 再 base64 編碼,得到 Sec-WebSocket-Accept。這個值是為了向客戶端證明伺服器真的認得這是 WebSocket handshake、也確認 request 來自真正的 web client 而非冒充者。伺服器回 101 Switching Protocols,帶 Upgrade: websocket、Connection: Upgrade、Sec-WebSocket-Accept 三個標頭。


推理因為上一段客戶端已經把「想升級」和一個隨機 Key 送過去了,所以作者接著站到伺服器這側:第一步是驗標頭——`Upgrade: websocket` 與 `Connection: Upgrade` 缺一不可;第二步是把 Key 變成一個客戶端能核對的答案。做法是把 Key 字串接上一個固定的 GUID、做 SHA-1、再 base64,得到 Sec-WebSocket-Accept。作者解釋這個計算的目的:證明伺服器真的認得 WebSocket handshake 並願意繼續。最後伺服器回 `101 Switching Protocols`,帶 `Upgrade: websocket`、`Connection: Upgrade` 回音客戶端的請求,以及算出的 `Sec-WebSocket-Accept`。畫面上的回應還多了 `Sec-WebSocket-Protocol: chat`,是伺服器從客戶端候選的 `chat, superchat` 裡選了一個。
- CSec-WebSocket-Accept = base64(SHA-1(Key + 固定 GUID)),證明伺服器讀過規格,不證明身分→ 畫地圖
- P自己算一次 Sec-WebSocket-Accept,確認公式與 RFC 範例一致→ 練習
- AKey/Accept 像接頭暗語:證明「你知道規矩」,不證明「你是誰」→ 批判類比
- ERFC 6455 範例:Key dGhlIHJhbXBsZSBub25jZQ== 算出 Accept s3pPLMBiTxaQ9kYGzzhZRbK+xOo=→ 存+演練
- R101 Switching Protocols:從這個回應之後,同一條連線改用 Upgrade 標頭指定的協定→ 存+回想
AI 補充作者跳過的關鍵一步是:這個計算為什麼能證明「伺服器懂 WebSocket」?因為 GUID 是 RFC 6455 寫死的常數 `258EAFA5-E014-47DA-95CA-C5AB0DC85B11`,只有讀過規格的實作才知道要接這一串再做 SHA-1;一個普通的 HTTP 伺服器或快取,就算把請求原樣回音,也算不出正確的 Accept。客戶端這邊會用同一個公式自己算一次,比對是否相同。以畫面上的 Key `dGhlIHJhbXBsZSBub25jZQ==` 為例,接上 GUID 後 SHA-1 再 base64 就是 `s3pPLMBiTxaQ9kYGzzhZRbK+xOo=`(這正是 RFC 裡的範例)。所以整個機制防的是「錯把非 WebSocket 伺服器當成 WebSocket 伺服器」以及舊 proxy 的誤回應,而不是身分驗證:Key 是明文送的、公式是公開的,任何程式都能算,作者說它能確認 request 來自「真正的 web client 而非惡意程式」這點在勘誤裡說明。另外 SHA-1 在這裡不涉及安全強度——它不是拿來簽章或加密,只是把「Key + GUID」壓成固定長度的指紋,就算 SHA-1 已被視為不安全的雜湊,這個用途仍然沒問題。注意 210 秒畫面上的 GUID 打錯了(`4D3N-BAA1-F78C42FB7FAF`,N 不是十六進位),實際值以上面 RFC 的為準。
「the server can confirm that the hand check request originated from a genuine web client and not a malicious actor attempting to impersonate a websocket connection」
4. 握手完成、失敗條件與 URL 格式 4:05–5:32
客戶端收到正確的 Sec-WebSocket-Accept 就驗證通過,雙方開始在同一條 TCP 連線上用 WebSocket 協定交換資料,HTTP 連線等於被 WebSocket 連線取代。若 Accept 值不對、標頭缺少、或狀態碼不是 101,連線就不建立、不送 frame,客戶端必須關閉。作者接著說明 URL 格式:ws 預設 port 80、wss 預設 443,wss 會先做 TLS handshake。

推理因為上一段伺服器已經回了 101 加 Accept,所以作者接著講客戶端的收尾:核對 Accept 正確就算 handshake 成功,之後雙方在「同一條已建立的 TCP 連線」上改講 WebSocket 協定——作者用「HTTP 連線基本上被 WebSocket 連線取代」這句話收掉第 1 段的伏筆:連線沒換,只是上面跑的協定換了。反面條件也列清楚:Accept 值不符、標頭缺少、狀態碼不是 101,三者任一發生就不建立連線、不送 frame,客戶端必須關掉。handshake 講完,作者回頭補第 2 段只帶過一下的 URL:ws 與 wss 對應 http 與 https,port 可省略,wss 會先做 TLS handshake。
- A「HTTP 連線被 WebSocket 取代」:同一條電話線,講到一半換一種語言→ 批判類比
- Rhandshake 失敗的三個條件:Accept 不符、標頭缺少、狀態碼不是 101 → 不建連線、不送 frame、客戶端關掉→ 存+回想
- Rws:// 預設 port 80、wss:// 預設 443(影片說 port 0 是口誤);URL 不允許 #fragment→ 存+回想
- Rwss 的順序:TCP 三向交握 → TLS handshake → 才送 HTTP upgrade GET,所以 handshake 本身也是加密的→ 存+回想
AI 補充「同一條 TCP 連線」是重點:客戶端不是收到 101 後另開一條連線,而是 101 回應結尾的空行之後,同一個 socket 裡的下一個位元組就開始依 WebSocket 格式解讀。這也是為什麼失敗時「不送 frame」——frame 是 WebSocket 的資料單位,連線沒升級就沒有它的存在空間。作者說「default Port 0 is used for ws」是口誤或字幕錯誤,正確是 ws 預設 port 80、wss 預設 443(和 http/https 完全一樣),勘誤裡有列。畫面(309 秒)的格式來自 RFC 6455:`ws://host[:port]path[?query]`、`wss://host[:port]path[?query]`;作者沒說的是 WebSocket URL 不允許 `#fragment`。wss 的「先做 TLS handshake」意思是順序為:TCP 三向交握 → TLS 交握 → 才送這段講的 HTTP upgrade GET,所以整個 WebSocket handshake 本身也是加密的;作者說 handshake「ensures a secure and authenticated connection」則說重了——安全來自 TLS(只有 wss 有),身分驗證要靠 Origin、cookie 或 token,Key/Accept 本身不提供這兩者(見第 3 段的勘誤)。
術語:WebSocket frame
「the port component is optional as default Port 0 is used for ws and 443 is used for WSS」
「the web socket handic ensures a secure and authenticated Connection by including a random key SEC websocket key generated by the client and verified by the server」
5. WebSocket frame 的結構 5:32–6:50
WebSocket 資料以一連串 frame 傳送,frame 是雙方交換的基本單位。欄位有:FIN 表示是否為訊息的最後一個片段;RSV1–3 保留位元通常為 0;opcode 定義資料類型(UTF-8 文字、二進位、ping 等);Mask 位元表示 payload 是否遮罩,客戶端到伺服器一律遮罩;payload length 依大小而異;masking key 用來遮罩;payload data 是實際內容。

推理因為上一段確立了 handshake 之後這條 TCP 連線上流動的不再是 HTTP 文字、而是一個接一個的 frame,所以作者接著把一個 frame 從頭到尾攤開:FIN 說這是不是訊息的最後一片、RSV1–3 保留位元通常為 0、opcode 說 payload 是文字、二進位還是 ping 之類的控制訊號、Mask 位元說 payload 有沒有被遮罩(客戶端到伺服器一律遮罩)、payload length 說內容多長、masking key 是遮罩用的鑰匙、payload data 才是真正的內容。作者在兩個欄位刻意留白:FIN 說「fragment 稍後再談」、Mask 說「masking 等一下仔細看」——這兩個「為什麼」就是後面要收的線索。
- CWebSocket frame:升級後的資料單位,標頭依序 FIN、RSV、opcode、Mask、length、key,再接 payload→ 畫地圖
- P照位元圖讀一個 frame:從第一個 bit 到 payload,每個欄位對一次→ 練習
- E344 秒的實例 frame:FIN=1、RSV 000、opcode 0001(文字)、Mask=1、length 13、32 位元 key→ 存+演練
- Rpayload length 的三段式:0–125 直接是長度;126 → 再讀 16 bit;127 → 再讀 64 bit→ 存+回想
- Ropcode 常見值與控制 frame→ 存+回想
AI 補充畫面(344 秒)是一個實際的 frame 位元圖,可以逐欄對照:第一個位元 FIN=1(這是完整訊息的最後一片,也就是沒有分片)、RSV 三個 0、opcode `0001`(文字 frame)、Mask=1(有遮罩,所以是客戶端送出的)、payload length `0001101`=13 個位元組,接著 32 位元的 masking key,最後是 payload。作者說 payload length「從 0 到 125」是把規則講了一半:那 7 個位元若是 0–125 就直接是長度;若是 126,後面再接 16 位元的實際長度;若是 127,再接 64 位元。這樣設計是為了讓小訊息只花 2 個位元組的標頭,大訊息又不受限。opcode 的常見值:`0x0` 接續片段、`0x1` 文字(必須是合法 UTF-8)、`0x2` 二進位、`0x8` 關閉、`0x9` ping、`0xA` pong,其中 0x8–0xA 是控制 frame。masking key 只有在 Mask=1 時才存在,所以伺服器送給客戶端的 frame 標頭會少 4 個位元組。「一律遮罩」是規格的硬性要求:伺服器收到未遮罩的客戶端 frame 必須關閉連線;反過來伺服器送出的 frame 不可以遮罩。
6. Masking:讓流量看起來不像 HTTP 6:50–8:00
Masking 的目的是讓即時資料能順利穿過那些不是為 WebSocket 設計的網路基礎設施。WebSocket 標準化之前,專為 HTTP 設計的 caching proxy 會把伺服器回應存起來重用;WebSocket 不遵循 request/response 模式,未遮罩的資料可能被誤認成 HTTP 內容而被快取,之後真正的 HTTP request 來時就出問題。遮罩讓 frame 內容不可預測、不像合法 HTTP,proxy 就不會嘗試快取或修改它。
推理因為上一段已經知道 masking 是用 frame 裡的 key 做可逆的混淆、不是加密,所以作者接著把目的講清楚:WebSocket 標準化之前,網路上很多為 HTTP 設計的 caching proxy 會把伺服器回應存起來、期待之後對類似 request 重用。WebSocket 不走 request/response,未遮罩的資料若長得像 HTTP,就可能被 proxy 誤認成 HTTP 內容存下來,等真的 HTTP request 來時吐出錯的東西。遮罩讓 frame 內容變得不可預測、不像任何合法的 HTTP 訊息,proxy 就不會想去快取或改寫它。一句話:masking 是讓 WebSocket 流量「明顯不是 HTTP」的手段。
- Cmasking:瀏覽器用腳本無法指定的隨機 key 對 payload XOR,讓線上位元組不可預測、排不成 HTTP,防舊 caching proxy 被污染→ 畫地圖
- Amasking 不是加密:key 就放在同一個 frame 裡,誰都能解——這是設計本意→ 批判類比
- E2010 年 Huang 的 cache poisoning:惡意網頁經 WebSocket 送出長得像 HTTP 的文字,proxy 誤快取後污染他人→ 存+演練
- Rmasking 的方向規則:客戶端→伺服器一律遮罩,伺服器收到未遮罩必須關連線;伺服器→客戶端不可遮罩→ 存+回想
AI 補充作者說了「像 HTTP 會被誤快取」,但漏了最關鍵的一環:誰能讓 frame「像 HTTP」?答案是攻擊者控制的網頁腳本。payload 是應用程式自己填的,惡意網站可以讓瀏覽器透過 WebSocket 送出一段刻意排成 `GET /script.js HTTP/1.1 ...` 的文字,再讓 proxy 以為那是一個 HTTP 請求/回應並快取——這就是 2010 年 Huang 等人示範的 cache poisoning 攻擊:其他經過同一台 proxy 的使用者會拿到被污染的 script。masking 的重點因此在於「key 由瀏覽器隨機產生、腳本無法指定」:腳本雖能決定 payload 原文,卻不知道出線時會跟哪個 key XOR,所以無法控制實際在網路上出現的位元組,也就排不出像 HTTP 的樣子。這也解釋了上一段留下的兩件事:為什麼 key 每個 frame 都要重新隨機(固定 key 就能被反推)、為什麼只有客戶端→伺服器要遮罩(威脅來自不受信任的瀏覽器腳本,伺服器端的程式本來就受信任)。而「key 放在同一個 frame 裡誰都能解」正是設計本意——它從來不是要保密,只是要讓「發送方以外的人」(包括腳本本身)無法預測線上的位元組。用 wss(TLS)時 proxy 根本看不到內容,但規格為求一致仍要求遮罩。
7. Fragmentation:大訊息分片與 FIN 位元 8:00–9:19
Fragmentation 是把大訊息切成小塊傳送,避免超過客戶端或伺服器的 buffer 上限造成 buffer overflow;例如透過 WebSocket 上傳大檔案,不分片就會整個檔案當一則訊息送。分片也讓接收端可以邊收邊處理,適合長時間流程的即時更新。實作上除了最後一個 frame,其餘 frame 的 FIN 都是 0,接收端看到 0 就繼續等;最後一個 frame FIN 為 1,表示訊息結束。

推理因為 masking 已經解釋完、只剩 FIN 與 fragment 的關係沒收,所以作者接著講 fragmentation:把一則大訊息切成小塊送。動機有兩個——第一,避免一則超大訊息(例如上傳大檔案)整個當成一個 frame 送出,撐爆客戶端或伺服器的 buffer;第二,讓接收端能邊收邊處理,早到的片段先用,適合長時間流程的即時更新。機制就是第 5 段的 FIN:除了最後一個 frame,其餘每片 FIN 都是 0,接收端看到 0 就繼續等;最後一片 FIN 為 1,表示這則訊息到此結束。畫面(534 秒)畫的正是一串片段從 User 逐步送到 Server、標著「Gradual delivery」。
- Cfragmentation:大訊息切成多個 frame,第一片帶型別 opcode、後續 0x0 continuation、只有最後一片 FIN=1→ 畫地圖
- A分片像串流上傳:不用等整個檔案準備好、也不用一次配整段記憶體→ 批判類比
- E分片的兩個動機:避免超大訊息撐爆 buffer;讓接收端邊收邊處理(534 秒畫面「Gradual delivery」)→ 存+演練
- R分片的交錯規則:兩則資料訊息的片段不可交錯(A1 A2 B1 A3 不合法),但控制 frame 可以插在片段之間→ 存+回想
AI 補充作者只講了 FIN,漏了另一半:分片時 opcode 怎麼填。第一片帶真正的型別(`0x1` 文字或 `0x2` 二進位),後面每一片 opcode 都是 `0x0`(continuation),接收端才知道這些 frame 屬於同一則訊息、該接在前一片後面。規格還規定同一條連線上兩則資料訊息的片段不可交錯(A1、A2、B1、A3 不合法),但控制 frame(ping、pong、close)可以插在片段之間——這就是分片的第二個好處:大訊息傳到一半時仍能回 pong 保活。「防 buffer overflow」要精確一點說:payload length 欄位本身可以表到 2^63,所以不是「送不了」,而是接收端得先配好整段記憶體;分片讓發送端在訊息總長還不知道時(例如串流產生的資料)就能開始送,接收端也能用固定大小的 buffer 逐片處理。實務上瀏覽器的 WebSocket API 不讓你控制分片,都是實作自己決定;伺服器端函式庫(如 Python 的 websockets、Node 的 ws)才會暴露分片選項。
術語:continuation frame
8. 應用場景與結語 9:19–10:21
作者列舉 WebSocket 的應用:聊天軟體(他的 WhatsApp 系統設計影片有講)、網頁即時更新、多人遊戲、多人協作畫布、股票即時報價。最後鼓勵觀眾拆解、實驗、理解每個功能背後的「為什麼」,才是真正精通。
推理因為前面七段已經把 WebSocket 從 handshake 到 frame、masking、fragmentation 全部拆完,所以作者接著回到「這些東西是為了什麼」:聊天軟體(他的 WhatsApp 系統設計影片用過)、網頁上的即時變更與更新、多人遊戲、多人共同編輯的即時畫布、證券交易所的股價報價。這五個場景的共同點正是第 1 段的痛點——伺服器需要主動推、而且推得頻繁,HTTP 的問答模式做不到。最後他把整支影片的態度說出來:拆開、實驗、理解每個功能背後的 why,才是真正精通——這正是本片對 masking 與 fragmentation 都先問「為什麼」的做法。
- E五個場景:聊天、網頁即時更新、多人遊戲、協作畫布、股價報價——共同點是伺服器要主動推且推得頻繁→ 存+演練
- R什麼時候不該用 WebSocket:單向推用 SSE、請求回應式 API 用 HTTP/2;WebSocket 的代價是每條連線都是伺服器上的長期狀態→ 存+回想
AI 補充把五個場景對回前面的機制會更清楚:聊天與股價報價是「伺服器主動推、訊息小而頻繁」,吃的是持續連線(第 1 段)與小訊息只要 2 位元組標頭(第 5 段 payload length 的設計);多人遊戲與協作畫布是「雙向、低延遲、順序敏感」,靠的是同一條 TCP 上的有序傳輸(第 4 段);上傳大檔案或串流更新則是 fragmentation(第 7 段)的用武之地。作者沒提的是什麼時候不該用 WebSocket:若只需要伺服器單向推、不需客戶端頻繁回話,Server-Sent Events(SSE)更簡單且走純 HTTP;若是大量請求/回應式的 API,HTTP/2 的多工已經解掉連線成本;WebSocket 的代價是每條連線都是伺服器上的長期狀態,擴展時要處理連線親和性與斷線重連。另外 2024 年後 HTTP/3 上的 WebTransport 開始成為即時通訊的另一個選項,但 WebSocket 因為簡單、支援廣,仍是預設選擇。
4. 總結
作者先把 WebSocket 定位成獨立的 TCP 協定、與 HTTP 的關係只有 handshake,於是沿著 handshake 走一遍:客戶端送 GET 帶 Upgrade、Connection、Sec-WebSocket-Key → 伺服器把 Key 接固定 GUID 做 SHA-1 再 base64 算出 Sec-WebSocket-Accept 並回 101 → 客戶端核對 Accept 後,同一條 TCP 上的協定從 HTTP 換成 WebSocket;任何一項不符就不建立連線。連線升級後資料以 frame 流動,作者逐欄位拆 FIN、RSV、opcode、Mask、payload length、masking key、payload data,並在 Mask 與 FIN 埋下兩個伏筆:masking 是為了讓流量長得不像 HTTP、避免舊式 caching proxy 誤快取;fragmentation 是為了避免大訊息撐爆 buffer 並讓接收端邊收邊處理,FIN 位元就是分片結束的訊號。最後以聊天、即時更新、多人遊戲、協作畫布、股價報價收尾。
勘誤總整理
確定錯誤/已過時見仁見智(取決於版本或情境)
| 段落 | 原話(transcript 逐字) | 說明 |
|---|---|---|
| 4. 握手完成、失敗條件與 URL 格式 5:13 |
「the port component is optional as default Port 0 is used for ws and 443 is used for WSS」 | ws 的預設 port 是 80,不是 0(port 0 在 TCP 裡是保留值,不能當目的埠)。wss 預設 443 是對的。這很可能是口誤或自動字幕聽錯(80 → 0),但照字面聽會學到錯的數字,故列出。 依據: RFC 6455 §3:「The port component is OPTIONAL; the default for "ws" is port 80, while the default for "wss" is port 443.」 |
| 3. 伺服器驗證並算出 Sec-WebSocket-Accept 3:09 |
「the server can confirm that the hand check request originated from a genuine web client and not a malicious actor attempting to impersonate a websocket connection」 | Sec-WebSocket-Key / Accept 的機制無法證明客戶端「真的是 web client」或「不是惡意程式」:Key 是客戶端隨意產生的明文,任何程式(curl、自寫腳本)都能送出合法的 Key 並通過驗證。RFC 6455 說明這個機制的目的是讓客戶端確認伺服器真的理解 WebSocket 協定、避免非 WebSocket 伺服器或快取的誤回應,並防止瀏覽器裡的腳本被用來對非 WebSocket 服務發動跨協定攻擊;要辨識客戶端身分得靠 Origin 檢查、cookie、token 等其他手段。 依據: RFC 6455 §1.3、§4.2.2、§10.3(2011) |
| 4. 握手完成、失敗條件與 URL 格式 4:43 |
「the web socket handic ensures a secure and authenticated Connection by including a random key SEC websocket key generated by the client and verified by the server」 | Sec-WebSocket-Key 與 Accept 的交換不提供「安全」也不提供「身分驗證」:Key 是明文、公式公開,任何客戶端都能通過。安全(加密、防竄改)來自 wss 的 TLS;客戶端身分驗證要靠 Origin 檢查、cookie、token 等應用層機制。Key/Accept 只證明伺服器真的理解 WebSocket 協定。 依據: RFC 6455 §1.3、§10.1–10.5(2011) |
5. 推薦三個下一步
1. 往下挖深:讀 RFC 6455 與 opcode / 控制 frame
影片只帶過 opcode 的三種值與 ping;close、pong、continuation frame 的規則以及 payload length 的 126/127 擴充格式都沒講,這是看懂抓包結果的缺口。
YouTube 搜尋:RFC 6455 WebSocket explained WebSocket opcode ping pong close frame WebSocket frame Wireshark
2. 往旁邊對照:SSE、long polling 與 HTTP/2 push
作者把 WebSocket 定位成「伺服器要主動推」的解法,但沒比較其他推送方式;知道 SSE 單向、HTTP/2 多路復用的取捨,才選得對。
YouTube 搜尋:WebSocket vs SSE vs long polling Server-Sent Events explained HTTP/2 vs WebSocket
3. 往上應用:把 WebSocket 放進系統設計
handshake 與 frame 是單一連線的機制;影片結尾的聊天、股價場景在多台伺服器、百萬連線時會遇到狀態同步、心跳、重連問題。
YouTube 搜尋:scale WebSocket millions connections WebSocket heartbeat reconnect chat system design WebSocket
- 接著看 ←15 Fullstack Concepts Every Frontend Should Know · 那站把 WebSocket 與 HTTP、TCP、SSE 並列點名,這站把 handshake 與 frame 拆到位元
- 接著看 →How to scale WebSockets to millions of connections · 這站講完一條連線怎麼建、資料怎麼走,那站講百萬條連線怎麼撐
- 相關 —How Frontend Engineers can master the Full-stack · 共用 HTTP 純文字請求/回應:那站教怎麼讀,這站的 handshake 就是一組 HTTP 文字
- 相關 —Real Senior Frontend System Design Interview 2026 (AI Coding Included) · 共用 WebSocket/SSE:這站給雙向連線的機制,那站在面試裡決定該不該選它
- 接著看 →WebSockets Aren’t as Reliable as You Think.. Here's Why · 先懂一條 WebSocket 連線怎麼建立,再看它在 server 崩潰、多台時會怎麼壞