WebSocket通訊的握手、傳輸、心跳到關閉,底層到底在做什麼?這篇把整個過程拆開來看。

WebSocket是什麼?

WebSocket是一種網路通訊協定,能在瀏覽器和伺服器之間建立全雙工的通訊管道。傳統HTTP是請求—回應模式,每次都要重新建立連線;WebSocket一旦握手完成,雙方就可以隨時互相傳送訊息,連線一直維持著。

WebSocket通訊過程

1. 建立連線

客戶端(通常是瀏覽器)先發出一個普通的 HTTP GET 請求,帶上升級用的標頭:

1
2
3
4
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

Sec-WebSocket-Key 是瀏覽器隨機產生的 16 bytes,經 base64 編碼後放進去,作用是讓伺服器證明它確實理解 WebSocket 協定。

2. 伺服器回應

伺服器若支援 WebSocket,回傳 HTTP 101(Switching Protocols):

1
2
3
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Sec-WebSocket-Accept 的計算方式:把客戶端的 key 和固定 GUID 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 串接後,SHA-1 雜湊,再 base64 編碼。這個設計是為了防止不懂 WebSocket 的 HTTP 代理或快取意外完成握手。

3. 連線建立完成

客戶端驗證 Sec-WebSocket-Accept 正確後,握手完成。從這一刻起,HTTP 協定退場,同一條 TCP 連線就變成 WebSocket 通道。

4. 資料傳輸

WebSocket 用「幀(Frame)」傳資料,幀標頭記錄了類型(文字或二進位)和長度。有一個細節值得注意:客戶端送往伺服器的幀必須做遮罩(masking),遮罩金鑰是每幀隨機產生的 32 bits;伺服器回傳的幀則不需要遮罩。這是 RFC 6455 強制規定的,伺服器收到沒有遮罩的客戶端幀必須直接關閉連線。

雙方都可以在不等待對方回應的情況下傳資料,這就是全雙工的意思。

5. 心跳機制

長時間沒有流量時,NAT 或防火牆可能靜悄悄地把連線砍掉。WebSocket 的 Ping/Pong 控制幀就是用來偵測這件事:任何一方都可以發 Ping,對方收到後必須立刻回 Pong。實務上通常是伺服器定期對所有連線發 Ping,超時沒收到 Pong 就主動關閉,釋放資源。

6. 關閉連線

關閉是有序的四步握手:

  1. 任何一方發出帶狀態碼的關閉幀
  2. 另一方收到後,回一個關閉幀
  3. 雙方都確認後,底層 TCP 連線才正式關閉

這樣可以確保兩端都知道連線結束,避免資料在關閉過程中遺失。

和 HTTP 輪詢相比

傳統做法是讓前端每隔幾秒發一次 HTTP 請求問「有新資料嗎?」,每次都要建立連線、帶上完整的 HTTP 標頭、等待回應,就算伺服器沒有新東西也是一樣的成本。

WebSocket 握手只做一次,之後的每筆訊息只有幾個 bytes 的幀標頭,伺服器也可以主動推資料,不用等客戶端來問。對需要高頻更新的場景,這個差距很明顯。

適用場景

WebSocket特別適合需要即時更新的應用,例如:

  • 線上遊戲
  • 即時聊天室
  • 股票行情顯示器
  • 線上協作工具