WebSocket

WebSocket 是一種網路傳輸協議，可在單個 TCP 連線上進行全雙工通訊，位於 OSI 模型的應用層。WebSocket 協議在 2011 年由 IETF 標準化為 RFC 6455，後由 RFC 7936 補充規範。中的 WebSocket API 由 W3C 標準化。

WebSocket 使得客戶端和伺服器之間的資料交換變得更加簡單，允許服務端主動向客戶端推送資料。在 WebSocket API 中，瀏覽器和伺服器只需要完成一次握手，兩者之間就可以建立永續性的連線，並進行雙向資料傳輸。

簡介

WebSocket 是一種與 HTTP 不同的協議。兩者都位於 OSI 模型的應用層，並且都依賴於傳輸層的 TCP 協議。

雖然它們不同，但是 RFC 6455 中規定：it is designed to work over HTTP ports 80 and 443 as well as to support HTTP proxies and intermediaries（WebSocket 透過 HTTP 埠 80 和 443 進行工作，並支援 HTTP 代理和中介），從而使其與 HTTP 協議相容。

為了實現相容性，WebSocket 握手使用 HTTP Upgrade 頭從 HTTP 協議更改為 WebSocket 協議。

WebSocket 協議支援 Web 瀏覽器（或其他客戶端應用程式）與 Web 伺服器之間的互動，具有較低的開銷，便於實現客戶端與伺服器的即時資料傳輸。

伺服器可以透過標準化的方式來實現，而無需客戶端首先請求內容，並允許訊息在保持連線開啟的同時來回傳遞。透過這種方式，可以在客戶端和伺服器之間進行雙向持續對話。

通訊透過 TCP 埠 80 或 443 完成，這在防火牆阻止非 Web 網路連線的環境下是有益的。另外，Comet 之類的技術以非標準化的方式實現了類似的雙向通訊。

大多數瀏覽器都支援該協議，包括 Google Chrome、Firefox、Safari、Microsoft Edge、Internet Explorer 和 Opera。

與 HTTP 不同，WebSocket 提供全雙工通訊。此外，WebSocket 還可以在 TCP 之上實現訊息流。TCP 單獨處理位元組流，沒有固有的訊息概念。

在 WebSocket 之前，使用 Comet 可以實現全雙工通訊。但是 Comet 存在 TCP 握手和 HTTP 頭的開銷，因此對於小訊息來說效率很低。WebSocket 協議旨在解決這些問題。

WebSocket 協議規範將 ws（WebSocket）和 wss（WebSocket Secure）定義為兩個新的統一資源識別符號（URI）方案，分別對應明文和加密連線。除了方案名稱和片段 ID（不支援 #）之外，其餘的 URI 元件都被定義為此 URI 的通用語法。

使用瀏覽器開發人員工具，開發人員可以檢查 WebSocket 握手以及 WebSocket 框架。

歷史

WebSocket 最初在 HTML5 規範中被引用為 TCPConnection，作為基於 TCP 的套接字 API 的佔位符。2008 年 6 月，進行了一系列討論，最終形成了稱為 WebSocket 的協議。

“WebSocket” 這個名字是 Ian Hickson 和 Michael Carter 之後在 #whatwg IRC 聊天室創造的，隨後由 Ian Hickson 撰寫並列入 HTML5 規範，並在 Michael Carter 的 Cometdaily 部落格上宣佈。

2009 年 12 月，Google Chrome 4 是第一個提供標準支援的瀏覽器，預設情況下啟用了 WebSocket。WebSocket 協議的開發隨後於 2010 年 2 月從 W3C 和 WHATWG 小組轉移到 IETF，並在 Ian Hickson 的指導下進行了兩次修訂。

該協議被多個瀏覽器預設支援並啟用後，RFC 於 2011 年 12 月在 Ian Fette 下完成。

背景

早期，很多網站為了實現推送技術，所用的技術都是輪詢。輪詢是指由瀏覽器每隔一段時間（如每秒）向伺服器發出 HTTP 請求，然後伺服器返回最新的資料給客戶端。這種傳統的模式帶來很明顯的缺點，即瀏覽器需要不斷的向伺服器發出請求，然而 HTTP 請求與回覆可能會包含較長的頭部，其中真正有效的資料可能只是很小的一部分，所以這樣會消耗很多頻寬資源。

比較新的輪詢技術是 Comet。這種技術雖然可以實現雙向通訊，但仍然需要反覆發出請求。而且在 Comet 中普遍採用的 HTTP 長連線也會消耗伺服器資源。

在這種情況下，HTML5 定義了 WebSocket 協議，能更好的節省伺服器資源和頻寬，並且能夠更即時地進行通訊。

Websocket 使用 ws 或 wss 的統一資源標誌符（URI）。其中 wss 表示使用了 TLS 的 Websocket。如：

ws://example.com/wsapi

wss://secure.example.com/wsapi

Websocket 與 HTTP 和 HTTPS 使用相同的 TCP 埠，可以繞過大多數防火牆的限制。預設情況下，Websocket 協議使用 80 埠；執行在 TLS 之上時，預設使用 443 埠。

優點

較少的控制開銷。在連線建立後，伺服器和客戶端之間交換資料時，用於協議控制的資料包頭部相對較小。在不包含擴充套件的情況下，對於伺服器到客戶端的內容，此頭部大小隻有 2 至 10 位元組（和資料包長度有關）；對於客戶端到伺服器的內容，此頭部還需要加上額外的 4 位元組的掩碼。相對於 HTTP 請求每次都要攜帶完整的頭部，此項開銷顯著減少了。
更強的即時性。由於協議是全雙工的，所以伺服器可以隨時主動給客戶端下發資料。相對於 HTTP 請求需要等待客戶端發起請求服務端才能響應，延遲明顯更少；即使是和 Comet 等類似的長輪詢比較，其也能在短時間內更多次地傳遞資料。
保持連線狀態。與 HTTP 不同的是，Websocket 需要先建立連線，這就使得其成為一種有狀態的協議，之後通訊時可以省略部分狀態資訊。而 HTTP 請求可能需要在每個請求都攜帶狀態資訊（如身份認證等）。
更好的二進位制支援。Websocket 定義了二進位制幀，相對 HTTP，可以更輕鬆地處理二進位制內容。
可以支援擴充套件。Websocket 定義了擴充套件，使用者可以擴充套件協議、實現部分自定義的子協議。如部分瀏覽器支援壓縮等。
更好的壓縮效果。相對於 HTTP 壓縮，Websocket 在適當的擴充套件支援下，可以沿用之前內容的上下文，在傳遞類似的資料時，可以顯著地提高壓縮率。

握手協議

WebSocket 是獨立的、建立在 TCP 上的協議。

Websocket 透過 HTTP/1.1 協議的 101 狀態碼進行握手。

為了建立 Websocket 連線，需要透過瀏覽器發出請求，之後伺服器進行回應，這個過程通常稱為 “握手”（Handshaking）。

5.1 例子

一個典型的 Websocket 握手請求如下：

客戶端請求：

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

伺服器回應：

HTTP/1.1 101 Switching Protocols

Upgrade: websocket

Connection: Upgrade

Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Sec-WebSocket-Protocol: chat

5.2 欄位說明

Connection 必須設定 Upgrade，表示客戶端希望連線升級。
Upgrade 欄位必須設定 Websocket，表示希望升級到 Websocket 協議。
Sec-WebSocket-Key 是隨機的字串，伺服器端會用這些資料來構造出一個 SHA-1 的資訊摘要。把 “Sec-WebSocket-Key” 加上一個特殊字串 “258EAFA5-E914-47DA-95CA-C5AB0DC85B11”，然後計算 SHA-1 摘要，之後進行 Base64 編碼，將結果做為 “Sec-WebSocket-Accept” 頭的值，返回給客戶端。如此操作，可以儘量避免普通 HTTP 請求被誤認為 Websocket 協議。
Sec-WebSocket-Version 表示支援的 Websocket 版本。RFC6455 要求使用的版本是 13，之前草案的版本均應當棄用。
Origin 欄位是必須的。如果缺少 origin 欄位，WebSocket 伺服器需要回復 HTTP 403 狀態碼（禁止訪問）。
其他一些定義在 HTTP 協議中的欄位，如 Cookie 等，也可以在 Websocket 中使用。

瀏覽器支援

WebSocket 協議的安全版本在 Firefox 6、Safari 6、Google Chrome 14、Opera 12.10 和 Internet Explorer 10 中實現。詳細的協議測試套件報告列出了這些瀏覽器與特定協議方面的一致性。

Opera 11 和 Safari 5 中實現了較舊的，不太安全的協議版本，以及 iOS 4.2 中的 Safari 移動版本。OS7 中的 BlackBerry Browser 實現了 WebSockets。由於存在漏洞，它在 Firefox 4 和 5 以及 Opera 11 中被停用。

所有最新的瀏覽器支援最新規範（RFC 6455）的 WebSocket 協議。一個詳細的測試報告列出了這些瀏覽器支援的 Websocket 版本。

1 基於 Gecko 6–10 版本的瀏覽器的 WebSocket 物件為 “mozwebsocket”，需要新增額外的程式碼。

伺服器

在伺服器方面，網上都有不同對 websocket 支援的伺服器：

PHP - https://code.google.com/p/phpwebsocket/
jetty （版本 7 開始支援 websocket）
netty
Ruby - https://github.com/gimite/web-socket-ruby
Kaazing
Tomcat（7.0.27 支援 websocket，建議用 tomcat8，7.0.27 中的介面已經過時）
WebLogic （12.1.2 開始支援）
Node.js - https://github.com/Worlize/WebSocket-Node
Node.js - http://socket.io
Nginx
mojolicious
Python - https://github.com/abourget/gevent-socketio
Django
erlang - https://github.com/ninenines/cowboy