WebSocket協議中文版
- 2021 年 1 月 13 日
- 筆記
WebSocket協議中文版
摘要
WebSocket協議實現在受控環境中運行不受信任程式碼的一個客戶端到一個從該程式碼已經選擇加入通訊的遠程主機之間的全雙工通訊。用於這個安全模型是通常由web瀏覽器使用的基於來源的安全模型。該協議包括一個打開階段握手、接著是基本消息幀、TCP之上的分層(layered over TCP)。該技術的目標是為需要與伺服器全雙工通訊且不需要依賴打開多個HTTP連接(例如,使用 XMLHttpRequest或者 <iframe>和長查詢)的基於瀏覽器應用提供的一種機制。
本備忘錄狀態
這是一個Internet標準跟蹤文檔。
本文檔是互聯網工程任務組(IETF)的一個產物。它代表了IETF社區的共識。它已接受公共審查和已經被互聯網工程指導委員會(IESG)認可發布。Interent標準的更多資訊可以在RFC 5741的第而節找到。
本文檔的當前狀態資訊、任何勘誤表以及如何提供它的回饋,可於//www.rfc-editor.org/info/rfc6455獲得。
版權聲明
版權所有(C)2011 IETF信託和確認為文檔作者的人。保留所有權利。
本文檔遵守BCP 78和涉及IETF文檔(//trustee.ietf.org/license-info)的在本文檔發布之日起生效的IETF信託的法律條文。請仔細閱讀這些文檔,因為他們描述了關於本文檔的你的權利和限制。從本文檔中提取的程式碼組件必須包括描述在第四章和簡體BSD License文件。e的信託法律條文並提供,不保證描述在簡體BSD許可協議中。
目錄
1 引言
1.1 背景
1.2 協議概述
1.3 打開階段握手
1.4 關閉階段握手
1.5 設計理念
1.6 安全模型
1.7 與TCP和HTTP的關係
1.8 建立連接
1.9 使用WebSocket協議的子協議
2 一致性要求
2.1 術語和其他約定
3 WebSocket URI
4 打開階段握手
4.1 客戶端要求
4.2 伺服器端要求
4.2.1 讀取客戶端的打開階段握手
4.2.2 發送伺服器的打開階段握手
4.3 為握手中使用的新的頭欄位整理的ABNF
4.4 支援多個版本的WebSocket協議
5 數據幀
5.1 概述
5.2 基本幀協議
5.3 客戶端到伺服器掩碼
5.4 分片
5.5 控制幀
5.5.1 Close
5.5.2 Ping
5.5.3 Pong
5.6 數據幀
5.7 示例
5.8 可拓展性
6 發送和接收數據
6.1 發送數據
6.2 接收數據
7 關閉連接
7.1 定義
7.1.1 關閉WebSocket連接
7.1.2 啟動WebSocket關閉階段握手
7.1.3 關閉階段握手已啟動
7.1.4 WebSocket已關閉
7.1.5 WebSocket連接關閉程式碼
7.1.6 WebSocket連接關閉原因
7.1.7 失敗WebSocket連接
7.2 異常關閉
7.2.1 客戶端發起的關閉
7.2.2 服務端發起的關閉
7.2.3 從異常關閉中恢復
7.3 正常連接關閉
7.4 狀態碼
7.4.1 定義的狀態碼
7.4.2 保留的狀態碼範圍
8 錯誤處理
8.1 處理UTF-8編碼數據的錯誤
9 拓展
9.1 協商拓展
9.2 已知拓展
10 安全注意事項
10.1 非瀏覽器客戶端
10.2 Origin注意事項
10.3 攻擊基礎設施(掩碼)
10.4 實現特定限制
10.5 WebSocket客戶端驗證
10.6 連接的保密性和完整性
10.7 處理無效數據
10.8 使用SHA-1的WebSocket握手
11 IANA考慮
11.1 註冊新的URI模式
11.1.1 註冊「ws」模式
11.1.2 註冊「wss」模式
11.2 註冊「WebSocket」HTTP Upgrade關鍵字
11.3 註冊新的HTTP頭欄位
11.3.1 Sec-WebSocket-Key
11.3.2 Sec-WebSocket-Extensions
11.3.3 Sec-WebSocket-Accept
11.3.4 Sec-WebSocket-Protocol
11.3.5 Sec-WebSocket-Version
11.4 WebSocket拓展名註冊
11.5 WebSocket子協議名註冊
11.6 WebSocket版本號註冊
11.7 WebSocket關閉程式碼註冊
11.8 WebSocket操作嗎註冊
11.9 WebSocket幀頭位註冊
12 其他規範使用WebSocket協議
13 致謝
14 參考資料
14.1 參考標準
14.2 參考資料
1 引言
1.1 背景
本節是非規範的。
過去,創建需要需要在客戶端和服務之間的雙向通訊(例如,即時消息和遊戲應用)的web應用,需要一個濫用的HTTP來輪詢伺服器進行更新但以不同的HTTP調用發生上行通知(RFC6202)。
這將導致各種各樣的問題:
1、伺服器被迫為每個客戶端使用一些不同的底層TCP連接:一個用於發送資訊到客戶端和一個新的用於每個傳入消息。
2、 線路層協議有較高的開銷,因為每個客戶端-伺服器消息都有一個HTTP頭資訊。
3、客戶端腳本被迫維護一個傳出的連接到傳入的連接的映射來跟蹤回復。
一個簡單的辦法是使用單個TCP連接雙向傳輸。這是為什麼提供WebSocket協議。與WebSocket API結合[WSAPI],它提供了一個HTTP輪詢的替代來進行從web頁面到遠程伺服器的雙向通訊。
同樣的技術可以用於各種各樣的web應用:
遊戲、股票行情、同時編輯的多用戶應用、伺服器端服務以實時暴露的用戶介面等等。
WebSocket協議被設計來取代現有的使用HTTP作為傳輸層的雙向通訊技術,並受益於現有的基礎設施(代理、過濾、身份驗證)。這樣的技術被實現來在效率和可靠性之間權衡,因為HTTP最初的目的不是用於雙向通訊(參見[RFC6202]的進一步討論)。WebSocket協議試圖在現有的HTTP基礎設施上下文中解決現有的雙向HTTP技術目標;同樣,它被設計工作在HTTP埠80和443,也支援HTTP代理和中間件,即使這具體到當前環境意味著一些複雜性。但是,這種設計不限制WebSocket到HTTP,且未來的實現可以在一個專用的埠上使用一個更簡單的握手,且沒有再創造整個協議。最後一點是很重要的,因為交互消息的傳輸模式不精確地匹配標準HTTP傳輸並可能在相同組件上包含不常見的負載。
1.2 協議概述
本節是非規範的。
本協議有兩部分:握手和數據傳輸。
來自客戶端的握手看起來像如下形式:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: //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
來自客戶端的首行遵照Request-Line格式。來自伺服器的首行遵照Status-Line格式。Request-Line和Status-Line在[RFC2616]中定義。
在這兩種情況中一個無序的頭欄位集合出現在首行之後。這些頭欄位的意思指定在本文檔的第4章。另外的頭欄位也可能出現,例如cookie[RFC6265]。頭的格式和解析定義在[RFC2616]
一旦客戶端和伺服器都發送了它們的握手,且如果握手成功,接著開始數據傳輸部分。這是一個每一段都可以的雙向通訊信道,彼此獨立,隨意發生數據。
一個成功握手之後,客戶端和伺服器來回第傳輸數據,在本規範中提到的概念單位為「消息」。在線路上,一個消息是由一個或者多個幀組成。WebSocket的消息並不一定對應於一個特定的網路層幀,可以作為一個可以被一個中間件合併或分解的片段消息。
一個幀有一個相應的類型。屬於相同消息的每一幀包含相同類型的數據。從廣義上講,有文本數據類型(它被解釋為UTF-8[RFC3629]文本)、二進位數據類型(它的解釋是留給應用)、和控制幀類型(它是不準備包含用於應用的數據,而是協議級的訊號,例如應關閉連接的訊號)。這個版本的協議定義了六個幀類型並保留10個以備將來使用。
1.3 打開階段握手
本節是非規範的。
打開階段握手的目的是兼容基於HTTP的伺服器軟體和中間件,以便單個埠可以用於與伺服器交流的HTTP客戶端和與伺服器交流的WebSocket客戶端。最後,WebSocket客戶端的握手是一個HTTP Upgrade請求:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: //example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
依照[RC2616],握手中的頭欄位可能由客戶端按照任意順序發送,因此在接收的不同頭欄位中的順序是不重要的。
“Request-URI”的GET方法[RFC2616]用於識別WebSocket連接的端點,即允許從一個IP地址服務的多個域名,也允許由單台伺服器的多個WebSocket端點。
客戶端按照[RFC2616]在它的握手的|Host|頭欄位中包含主機名,以便客戶端和伺服器都能驗證他們同意哪一個正在使用的主機。
在WebSocket協議中另外的頭欄位可以用於選擇選項。典型的選項在這個版本中可用的是子協議選擇器(|Sec-WebSocket-Protocol|)、客戶端支援的擴展列表(|Sec-WebSocket-Extensions|)、|Origin|頭欄位等。|Sec-WebSocket-Protocol|請求頭欄位可以用來表示客戶端接受的子協議(WebSocket協議上的應用級協議層)。伺服器選擇一個可接受的協議,或不在乎它的握手中回應該值表示它已經選擇了那個協議。
Sec-WebSocket-Protocol: chat
|Origin| 頭欄位 [RFC6454]是用於保護防止未授權的被瀏覽器中使用WebSocket API的腳本跨域使用WebSocket伺服器。伺服器收到WebSocket連接請求生成的腳本來源。如果伺服器不想接受來自此源的連接,它可以選擇通過發送一個適當的HTTP錯誤碼拒絕該連接。這個頭欄位由瀏覽器客戶端發送,對於非瀏覽器客戶端,如果它在這些客戶端上下文中有意義,這個頭欄位可以被發送。
最後,伺服器要證明收到客戶端WebSocket握手的資訊,以便伺服器不接受不是WebSocket連接的連接。這可以防止一個通過使用XMLHttpRequest [XMLHttpRequest]或一個表單提交發送它精心製作的包欺騙WebSocket伺服器的攻擊者。
為了證明收到的握手,伺服器必須攜帶兩條資訊並組合它們並形成一個響應。第一天資訊源自客戶端握手中的|Sec-WebSocket-Key|頭資訊:
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
對於這個頭欄位,伺服器必須攜帶其值(出現在頭欄位上,如,減去開頭和結尾空格的base64編碼 [RFC4648]的版本)並將這個與字元串形成的全局唯一標識符(GUID, [RFC4122]) “258EAFA5-E914-47DA-95CA-C5AB0DC85B11″連接起來,其不太可能被不理解WebSocket協議的網路端點使用。SHA-1散列(160位)[FIPS.180-3],base64編碼(參見[RFC4648]第4章)、用於這個的一系列相關事務接著再伺服器握手過程中返回。
具體而言,如果在上面例子中,|Sec-WebSocket-Key|頭欄位的值為”dGhlIHNhbXBsZSBub25jZQ==”,伺服器將連接字元串”258EAFA5-E914-47DA-95CA-C5AB0DC85B11″形成字元串”dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11″。伺服器使用SHA-1散列這個,併產生值0xb3 0x7a 0x4f 0x2c 0xc0 0x62 0x4f 0x16 0x90 0xf6 0x46 0x06 0xcf 0x38 0x59 0x45 0xb2 0xbe 0xc4 0xea。這個值接著使用base64編碼(參見[RFC4648]第4章),產生值 “s3pPLMBiTxaQ9kYGzzhZRbK+xOo=”。這個值將接著在|Sec-WebSocket-Accept|頭欄位中回應。
來自伺服器的握手比客戶端握手更簡單。首行是一個HTTP Status-Line,具有狀態碼101:
HTTP/1.1 101 Switching Protocols:
101以外的任何狀態碼錶示WebSocket握手沒有完成且HTTP語義仍適用。頭資訊遵照該狀態碼。
|Connection| 和 |Upgrade|頭欄位完成HTTP升級。|Sec-WebSocket-Accept|頭欄位表示伺服器是否將接受該連接。如果存在,這個頭欄位必須包括客戶端在|Sec-WebSocket-Key|中現時發送的與預定義的GUID的散列。任何其他值不能被解釋為一個伺服器可接受的連接。
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
這些欄位由WebSocket客戶端為腳本頁面做檢查。如果|Sec-WebSocket-Accept|不能匹配盼望的值、如果頭欄位缺失或者HTTP狀態碼不是101,則連接不能建立,且WebSocket幀將不發生。
可選的欄位也可以包含在內。在這合適版本的協議中,主要可選欄位是|Sec-WebSocket-Protocol|,其表示伺服器選擇的子協議。WebSocket客戶端驗證伺服器包含的在WebSocket客戶端握手中指定的一個值。聲明多個子協議的伺服器必須確保它選擇第一個,基於客戶端握手並指定它在其握手中。
Sec-WebSocket-Protocol: chat
伺服器也可以設置cookie相關的可選欄位為_set_ cookies,描述在[RFC6265]。
1.4 關閉階段握手
本節是非規範的。
關閉階段握手比打開階段握手簡單得多。
兩個節點中的任一個都能發送一個控制幀與包含一個指定控制序列的數據來開始關閉階段的握手(詳見5.5.1節)。在收到這樣一個幀時,另一個節點在響應中發送一個Close幀,如果還沒有發送一個。在收到那個控制幀時,第一個節點接著關閉連接,安全地知道沒有更多的數據到來。
發送一個控制幀之後,表示連接將被關閉,一個節點不會發送任何更多的數據;在接收到一個控制幀之後,表示連接將被關閉,一個節點會丟棄收到的任何更多的數據。
對於兩個節點同時地初始化這個握手是安全的。
關閉階段握手的目的是完成TCP關閉握手(FIN/ACK),基於TCP關閉階段握手不總是可靠的端到端,尤其存在攔截代理和中間件。
通過發送一個Close幀並等待響應中的Close幀,某些情況下可避免數據不必要的丟失。例如,在某些平台上,如果一個socket關閉了,且接收隊列中有數據,一個RST包被發送了,這樣會導致接受RST的一方的recv()失敗,即使有數據等待讀取。
1.5 設計理念
本節是非規範的。
WebSocket協議應該以最小幀的原則設計(唯一存在的框架是使協議基於幀而不是基於流且支援區分Unicode文本和二進位)。期望通過應用層將元數據分層在WebSocket之上,通用的,通過應用層將元數據分層在TCP之上(例如HTTP)。
從概念上講,WebSocket只是TCP之上的一層,執行以下操作。
1、為瀏覽器添加一個web基於來源的安全模型
2、添加一個定址和協議命名機制來支援在一個IP地址的一個埠的多個主機名的多個服務。
3、在TCP之上分一個幀機制層已回到TCP基於的IP包機制,但沒有長度限制。
4、包括一個額外的帶內(in-band)關閉階段握手,其被設計來工作在現存的代理和其他中間件。
除此之外,WebSocket沒有添加任何東西。基本上,它的目的是儘可能接近僅暴露原始TCP到腳本,儘可能考慮到Web的約束。它也被設計為它的伺服器能與HTTP伺服器共享一個埠的這樣一種方式,通過持有它的握手是一個有效的HTTP Upgrade請求。一個可以在概念上使用其他協議來建立客戶端-伺服器消息,但WebSocket的意圖是提供一個相對簡單的協議,可以與現有的HTTP和部署的HTTP基礎設施(例如代理)同時存在,並儘可能接近TCP,且對於使用考慮到安全考慮的這樣的基礎設施同樣是安全的,有針對性的補充以簡化使用並保持簡單的事情簡單(如增加的消息語義)。
協議的目的是為了可拓展性:未來版本將可能引入額外的概念,如復用(multiplexing)。
1.6 安全模型
本節是非規範的。
WebSocket協議使用瀏覽器使用的來源模型限制web頁面可以與WebSocket伺服器通訊,當WebSocket協議是從一個web頁面使用。當然,當WebSocket協議被一個獨立的客戶端直接使用時(也就是,不是從瀏覽器中的一個web頁面),來源模型不再有用,因為客戶端可以提供任意隨意的來源字元串。
該協議的目的是無法與現有的協議,如SMTP [RFC5321]和HTTP建立一個連接,同時允許HTTP伺服器來選擇支援該協議如果想要。這是通過具有嚴格的和詳盡的握手和通過限制在握手完成之前能被插入到連接的數據(因此限制多少伺服器可以被應用)實現的。
當數據時來自其他協議時,同樣的目的是無法建立連接的,尤其發送到一個WebSocket伺服器的HTTP,例如,如果一個HTML表單提交到WebSocket伺服器可能會發生。這主要通過要求伺服器驗證它讀取的握手來實現,它只能做如果握手包含適當的部分,只能通過一個WebSocket客戶端發送。尤其是,在寫本規範的時候,|Sec-|開頭的欄位不能又web瀏覽器的攻擊者設置,僅能使用HTML和JavaScript APIs例如XMLHttpRequest [XMLHttpRequest]。
1.7 與TCP和HTTP的關係
本節是非規範的。
WebSocket協議是一個獨立的基於TCP的協議。它與HTTP唯一的關係是它的握手是由HTTP伺服器解釋為一個Upgrade請求。
默認情況下,WebSocket協議使用埠80用於常規的WebSocket連接和埠443用於WebSocket連接的在傳輸層安全(TLS) [RFC2818]之上的隧道化。
1.8 建立連接
本節是非規範的
當一個連接到一個HTTP伺服器共享的埠時(這種情況是很有可能在傳輸資訊到埠80和443出現),連接將出現在HTTP伺服器,是一個正常的具有一個Upgrade提議的GET請求。在相對簡單的安裝,只用一個IP地址和單台伺服器用於所有的數據傳輸到單個主機名,這可能允許一個切實可行的辦法對基於WebSocket協議的系統進行部署。在更複雜的安裝(例如,負載均衡和多伺服器),一組獨立的用於WebSocket連接的主機從HTTP伺服器分離出來可能更容易管理。在寫該規範的時候,應該指出的是,在埠80和443上的連接有明顯不同的成功率,對於在埠443上的連接時明顯更有可能成功,儘管這可能會隨著時間改變。
1.9 使用WebSocket協議的子協議
本節是非規範的。
客戶端可能通過包含|Sec-WebSocket-Protocol|欄位在它的握手中使用一個特定的子協議請求伺服器。如果它被指定,伺服器需要在它的響應中包含同樣的欄位和一個選擇的子協議值用於建立連接。
這些子協議名字應該按照11.5節被註冊。為了避免潛在的碰撞,推薦使用包含ASCII版本的子協議發明人的域名的名字。例如,如果Example公司要創建一個Chat子協議,由Web上的很多伺服器實現,它們可能命名它為”chat.example.com”。如果Example組織命名它們的競爭子協議為”chat.example.org”,那麼兩個子協議可能由伺服器同時實現,因為伺服器根據客戶端發送的值動態地選擇使用哪一個子協議。
通過改變子協議的名字,子協議可以以向後不兼容方式版本化,例如,要從”bookings.example.net” 到 “v2.bookings.example.net”。就WebSocket客戶端而言,這些子協議被視為是完全不同的。向後兼容的版本可以通過重用相同的子協議字元串實現,但要仔細設置實際的子協議以支援這種可拓展性。
2 一致性要求
在本規範中所有的圖表、示例和注釋是非規範的,以及所有章節明確地標記為非規範的。除此之外,在本規範中的一切是規範的。
該文檔中的關鍵字必須 “MUST”, 不能”MUST NOT”, 需要”REQUIRED”,應當 “SHALL”, 不得”SHALL NOT”,應該 “SHOULD”, 不應該”SHOULD NOT”, 推薦”RECOMMENDED”, 可能 “MAY”, 和可選的”OPTIONAL” 由 [RFC2119]中的描述解釋。
作為演算法的一部分的祈使句中的要求措辭(例如「去掉任何前導空格字元」或「返回false並終止這些步驟」)解釋為引入演算法中使用的關鍵字(“MUST”,”SHOULD”,”MAY”等)的意思。
作為演算法或特定的步驟的一致性要求措辭可以以任何形式實現,只要最終結果是相等的。(尤其是,定義在本規範中的演算法目的是容易遵循而不必是高性能的)。
2.1 術語和其他約定
_ASCII_指定義在[ANSI.X3-4.1986]中的字元編碼方案。
此文中提到的UTF-8值和使用 UTF-8標記法格式定義在 STD 63 [RFC3629]。
關鍵術語例如命名演算法或定義是表示像_this_。
頭欄位名字或變數表示像|this|。
變數值表示像/this/。
本文檔提及的程式_失敗WebSocket連接_。該程式定義在7.1.7節。
_將字元串轉換為ASCII小寫_意思是替換U+0041到 U+005A(也就是,拉丁文,大寫字母A到拉丁文大寫字母Z)範圍的所有字元為U+0061到 U+007A(也就是,拉丁文,小寫字母A到拉丁文小寫字母Z)範圍對應的字元。
以一個_ASCII不區分大小寫_方式比較兩個字元串意思是精確地比較它們,程式碼點對程式碼點,除了U+0041 到 U+005A(也就是,拉丁文,大寫字母A到拉丁文大寫字母Z)範圍中的字元,U+0061到 U+007A(也就是,拉丁文,小寫字母A到拉丁文小寫字母Z)範圍對應的字元被認為也匹配。
用於本文檔的術語「URI」定義在[RFC3986]。
當一個實現需要_發送_作為WebSocket一部分的數據,實現可能任意地推遲實際的傳輸,例如,緩衝數據為了發送更少的IP包。
注意,該文檔同時使用[RFC5234] 和 [RFC2616]的ABNF變體在不同章節。
3 WebSocket URIs
本規範定義了兩個URI方案,使用定義在RFC 5234 [RFC5234]中的ABNF句法、和術語和由URI規範RFC 3986 [RFC3986]定義的ABNF製品。
ws-URI = “ws:” “//” host [ “:” port ] path [ “?” query ]
wss-URI = “wss:” “//” host [ “:” port ] path [ “?” query ]
host = <host, defined in [RFC3986], Section 3.2.2>
port = <port, defined in [RFC3986], Section 3.2.3>
path = <path-abempty, defined in [RFC3986], Section 3.3>
query = <query, defined in [RFC3986], Section 3.4>
埠組件是可選的;用於「WS」的默認埠試80,而用於「WSS」默認埠時443。
如果方案組件不區分大寫匹配「wss」,URI被稱為「安全的」(它是說,「設置了安全標記」)。
“resource-name”(在4.1節也稱為/resource name/)可以通過連接一下來構造:
1、”/”如果路徑組件是空
2、 “/” if the path component is empty
3、路徑組件
4、the path component
5、 “?”如果查詢組件是空
6、”?” if the query component is non-empty
7、查詢組件
8、 the query component
片段標識符在WebSocket URI中是無意義的且必須不用在這些URI上。任何URI方法,字元「#」,當不表示片段開始時,必須被轉義為%23.
4 打開階段握手
4.1 客戶單需求
要_建立WebSocket連接_,客戶端打開一個連接並發送一個握手,就像本節中定義的那樣。一個連接最初被定義為一個CONNECTING狀態。客戶端將需要提供一個/host/, /port/,/resource name/, 和 /secure/標記,它們都是在第三章討論的WebSocket URI的組件,連同一起使用的一個/protocols/ 和 /extensions/列表。此外,如果客戶端是一個web瀏覽器,它提供/origin/。客戶端運行在一個受控環境,例如綁定到特定運營商的手機上的瀏覽器,可以下移(offload)連接管理到網路上的另一個代理。在這種情況下,用於本規範目的的客戶端被認為包括手機軟體和任何這樣的代理。
當客戶端要_建立一個WebSocket連接_,給定一組(/host/, /port/, /resource name/,和/secure/ 標記)、連同一起使用的一個/protocols/ 和 /extensions/列表、和在web瀏覽器情況下的一個/origin/,它必須打開一個連接、發送一個打開階段握手、並讀取伺服器響應中的握手。應如何打開連接的確切要求、在打開階段握手應發送什麼、以及應如何解釋伺服器響應,在本節如下所述。在下面的文本中,我們將使用第三章中的術語,如定義在那章中的”/host/” 和 “/secure/」標記。
1. 傳入該演算法的WebSocket URI組件(/host/, /port/, /resource name/,和 /secure/ 標記)根據指定在第三章的WebSocket URI規範,必須是有效的。如果任何組件是無效的,客戶端必須_失敗WebSocket連接_並終止這些步驟。
2. 如果客戶已經有一個倒通過主機/host/和埠/port/對標識的遠程主機(IP地址)的WebSocket連接,即使遠程主機是已知的另一個名字,客戶端必須等待直到連接已經建立或由於連接已經失敗。必須不超過一個連接處於CONNECTING狀態。如果同時多個連接到同一個IP地址,客戶端必須序列化它們,以致一次不多於連接在以下步驟中運行。
如果客戶端不能決定遠程主機的IP地址(例如,因為所有通訊是通過代理伺服器本身進行DNS查詢),那麼客戶端必須假定這步的目的是每一個主機名引用一個不同遠程主機,且相反,客戶端應該限制同時掛起的連接總數為一個適當低的數(例如,客戶端可能允許到a.example.com 和 b.example.com同時掛起連接,但如果30個同時連接到同一個請求的主機,那可能是不允許的)。例如,在一個web瀏覽器上下文中,客戶端需要考慮用戶已經打開的標籤數量,在設置同時掛起的連接數量的限制時。
注意:這使得它很難僅通過打開大量的WebSocket連接到遠程主機為腳本執行一個拒絕服務攻擊。當攻擊在關閉連接之前被暫停是,伺服器可以進一步降低自身的負載,因為這將降低客戶端重新連接的速度。
注意:沒有限制一個客戶端可以與單個遠程主機有的已建立的WebSocket連接數量。伺服器可以拒絕接受來自具有大量的現有連接的主機IP地址的連接或當遭受高負載時斷開佔用資源的連接。
3. _使用代理_:當有WebSocket協議連接主機/host/和埠/port/時,如果客戶端配置使用代理,那麼客戶端應該連接到代理並要求它打開一個倒由/host/給定主機和/port/給定埠的TCP連接。
例子:例如,如果客戶端所有的資訊傳輸使用一個HTTP代理,那麼如果它視圖連接到伺服器example.com的埠80,它可能會發送以下行到代理伺服器:
CONNECT example.com:80 HTTP/1.1
Host: example.com
如果還有密碼,連接可能看起來像:
CONNECT example.com:80 HTTP/1.1
Host: example.com
Proxy-authorization: Basic ZWRuYW1vZGU6bm9jYXBlcyE=
如果客戶端沒有配置使用一個代理,那麼應該打開一個直接TCP連接到由/host/給定的主機和/port/給定的埠。
注意:不暴露明確的UI來為WebSocket連接選擇一個獨立於其他代理的代理實現,鼓勵使用SOCKS5 [RFC1928]代理用於WebSocket連接,如果有的話,或做不到這一點,選擇為HTTPS連接配置代理勝過為HTTP連接配置代理。
為了代理自動配置腳本,傳給函數的URI必須從/host/, /port/,/resource name/和/secure/標記來構造,使用第三章給定的WebSocket URI定義。
注意:WebSocket協議可以在代理自動配置腳本中從模式中識別(「WS」用於未加密的連接和「wss」用於加密的連接)。
4. 如果連接無法打開,或者因為直接連接失敗或者因為任何使用的代理返回一個錯誤,那麼客戶端必須_失敗WebSocket連接_並終止連接嘗試。
5. 如果/secure/是true,客戶端必須在連接之上執行一個TLS握手在打開連接之後和發生握手數據之前[RFC2818]。如果這個失敗了(例如,伺服器的證書不能被驗證),那麼客戶端必須_失敗WebSocket連接_並終止連接。否則,所有在該通道山的進一步通訊必須通過加密隧道[RFC5246]。
客戶端必須在TLS握手中使用伺服器命名指示拓展[RFC6066]。
一旦一個到伺服器的連接(包括通過代理或在TLS加密隧道之上的連接),客戶端必須發送一個打開階段握手到伺服器。該握手包括一個HTTP Upgrade請求,連同一個必需的和可選的頭欄位列表。該握手的要求如下所示:
1. 握手必須是像[RFC2616]指定的那樣的有效的HTTP請求。
2. 請求方法必須是GET、且HTTP版本必須是至少1.1。
例如,如果WebSocket URI是”ws://example.com/chat”,發送的第一行應該是”GET /chat HTTP/1.1″。
3. 請求的”Request-URI”部分必須匹配定義在第三章的(一個相對URI)/resource name/或是一個絕對的http/https URI,當解析時,有一個/resource name/, /host/,和/port/匹配相應的ws/wss URI。
4. 請求必須包含一個|Host|頭欄位,其值包含/host/加上可選的”:”後跟/port/(當沒有默認埠時)。
5. 請求必須包含一個|Upgrade|頭欄位,其值必須包含”websocket”關鍵字。
6. 請求必須包含一個|Connection|頭欄位,其值必須包含”Upgrade”標記。
7. 請求必須包含一個名字為|Sec-WebSocket-Key|的頭欄位,這個頭欄位的值必須是臨時組成的一個隨機選擇的已經base64編碼的(參見[RFC4648]第4章)16位的值。臨時必須是為每個連接隨機選擇的。
注意:作為一個例子,如果隨機選擇的值是位元組序列0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10,頭欄位的值必須是”AQIDBAUGBwgJCgsMDQ4PEC==”。
8. 如果請求來自一個瀏覽器客戶端,請求必須包含一個名字為|Origin| [RFC6454]的頭欄位。如果連接是來自非瀏覽器客戶端,如果該客戶端的語義匹配描述在這的用於瀏覽器的使用情況時,請求可以包含這個欄位。該頭欄位的值是建立正在運行的連接程式碼中的環境的origin的ASCII序列化。參考[RFC6454]獲取如果構造該頭欄位的值的詳細資訊。
作為一個例子,如果從www.example.com下載的程式碼試圖建立到ww2.example.com的連接,該頭欄位的值將是”//www.example.com”。
9. 請求必須包含一個名字為|Sec-WebSocket-Version|的頭欄位。該頭欄位的值必須是13。
注意:儘管本文檔的草案版本(-09, -10, -11,和 -12)發布了(它們多不是編輯上的修改和澄清而不是改變電報協議),值09, 10, 11,和 12不被用作有效的Sec-WebSocket-Version。這些值被保留在IANA註冊中心,但並將不會被使用。
10. 請求可以包含一個名字為|Sec-WebSocket-Protocol|的頭欄位。如果存在,該值表示一個或者多個逗號分割的客戶端要表達的子協議,按優先順序排列,包含該值的元素必須是非空字元串,且字元在U+0021 到U+007E範圍內但不包含定義在[RFC2616]中的分割字元且必須所有是唯一的字元串。用於該頭欄位值的ABNF是1#token,其構造和規則定義在[RFC2616]給出。
11. 請求可以包含一個名字為|Sec-WebSocket-Extensions|的頭欄位。如果存在,該值表示客戶端想要表達的協議級的擴展。該頭欄位的解釋和格式描述在第9.1節。
12. 請求可以包含任意其他頭欄位,例如,cookies [RFC6265]和/或驗證相關的頭欄位,例如|Authorization頭欄位 [RFC2616],其根據定義它們的文檔處理。
一旦客戶端的打開階段握手已經發送,客戶端在發送任何進一步數據之前必須等待自伺服器的一個響應。客戶端必須驗證伺服器的響應,如下所示:
1. 如果收到的伺服器的狀態碼不是101,客戶端處理每個HTTP [RFC2616]程式的響應。尤其是,如果收到一個401狀態碼客戶端可能執行身份驗證;伺服器可能使用一個3XX狀態碼重定向客戶端(但客戶端不需要跟隨他們)等等。否則,按以下步驟處理。
2. 如果響應缺少一個|Upgrade|頭欄位或|Upgrade|頭欄位包含的值不是一個不區分大小寫的ASCII匹配值”websocket”,客戶端必須_失敗WebSocket連接_。
3. 如果想要缺少一個|Connection|頭欄位或者|Connection|頭欄位不包含一個不區分大小寫的ASCII匹配值”Upgrade”符號,客戶端必須_失敗WebSocket連接_。
4. 如果想要缺少一個|Sec-WebSocket-Accept|頭欄位或 |Sec-WebSocket-Accept| 包含一個不是|Sec-WebSocket-Key|(一個字元串,不是base64編碼的)與字元串 “258EAFA5-E914-47DA-95CA-C5AB0DC85B11″但忽略任何前導和結尾空格相關聯的base64編碼的SHA-1值,客戶端必須_失敗WebSocket連接_。
5. 如果響應包含一個|Sec-WebSocket-Extensions|頭欄位且頭欄位表示使用一個擴展但沒有出現在客戶端握手中(伺服器表示的一個擴展,不是客戶端請求的),客戶端必須_失敗WebSocket連接_。解析該頭欄位以確定請求了哪些擴展在9.1節討論。
6. 如果響應包含一個|Sec-WebSocket-Protocol|頭欄位且該頭欄位表示使用一個字協議但沒出現在客戶端握手中(伺服器表示的一個子協議,不是客戶端請求的),客戶端必須_失敗WebSocket連接_。
如果伺服器響應不符合定義在本節和4.2.2節中的伺服器握手的要求,客戶端必須_失敗WebSocket連接_。
請注意,根據[RFC2616],所有命名在HTTP請求和HTTP響應中的頭欄位是不區分大小寫的。
如果伺服器響應驗證了以上提供的,這是說,_WebSocket連接建立了_且WebSocket連接處於OPEN狀態。_使用中的擴展_被定義為一個(可能為空)字元串,其值等於伺服器握手中提供的|Sec-WebSocket-Extensions|頭欄位的值或如果在伺服器握手中沒有該頭欄位則為null值。_使用中的子協議_被定義為伺服器握手中的|Sec-WebSocket-Protocol|頭欄位的值或如果在伺服器握手中沒有該頭欄位則為null值。另外,如果在伺服器握手中表示cookie應該被設置(定義在[RFC6265])的任何頭欄位,這些cookie被稱為_在伺服器打開階段握手期間的Cookie設置。
4.2 伺服器端要求
伺服器可以下移(offload)連接管理到網路上的其他代理,例如,負載均衡和反向代理。在這樣的情況下,用於本規範的目的的伺服器被認為是包括伺服器端基礎設施的所有部分,從開始的設備到終止TCP連接,處理請求和發送響應的伺服器的所有方式。
例如:一個數據中心可能有一個用適當的握手來響應WebSocket請求的伺服器,並按照傳遞連接到另一個伺服器來真正處理數據幀。對於本規範的目的,「伺服器」是結合了兩種電腦。
4.2.1 讀取客戶端的打開階段握手
當客戶端開始一個WebSocket連接,它發送它的打開階段握手部分。伺服器必須至少解析這個握手為了獲取必要的資訊來生成伺服器握手部分。
客戶端打開階段握手包括以下部分。如果伺服器,當讀取握手時,發現客戶端沒有發送一個匹配下面描述的握手(注意,按照[RFC2616],頭欄位順序是不重要的),包括但不限制任何違反ABNF語法指定的握手組件,伺服器必須停止處理客戶端握手並返回一個具有一個適當錯誤碼的(例如400錯誤的請求)HTTP響應。
1. 一個HTTP/1.1或更高版本的GET請求,包括一個”Request-URI” [RFC2616]應該被解釋為定義在第3幢的/resource name/(或一個包含/resource name/的絕對HTTP/HTTPS URI)。
2. 一個|Host|頭欄位包含伺服器的許可權。
3. 一個|Upgrade|頭欄位包含值”websocket”,視為一個不區分大小寫的ASCII值。
4. 一個|Connection|頭欄位包含符號”Upgrade”,視為一個不區分大小寫的ASCII值。
5. 一個|Sec-WebSocket-Key|頭欄位,帶有一個base64編碼的值(參見[RFC4648]第4章),當解碼是,長度是16位元組。
6. 一個|Sec-WebSocket-Version|頭欄位,帶有值13.
7. 可選的,一個|Origin|頭欄位。該頭欄位由所有瀏覽器客戶端發送。一個試圖缺失此頭欄位的連接不應該被解釋為來自瀏覽器客戶端。
8. 可選的,一個|Sec-WebSocket-Protocol|頭欄位,帶有表示客戶端想要表達的協議的值列表,按優先順序排列。
9. 可選的,一個|Sec-WebSocket-Extensions|頭欄位,帶有表示客戶端想要表達的擴展的值列表。此頭欄位的解釋在9.1節討論。
10. 可選的,其他頭欄位,例如這些用於發送cookie或請求伺服器身份驗證的。未知的頭欄位被忽略,按照[RFC2616]。
4.2.2 發送伺服器的打開階段握手
當客戶端建議一個到伺服器的WebSocket連接,伺服器必須完成以下步驟來接受該連接並發送伺服器的打開階段握手。
1. 如果連接發送在一個HTTPS (HTTP-over-TLS)埠上,在連接之上執行一個TLS握手。如果失敗了(例如,在擴展的客戶端hello “server_name”擴展中的客戶端指示的一個主機名,伺服器對主機不可用),則關閉該連接;否則,用於該連接的所有進一步的通訊必須貫穿加密的隧道[RFC5246]。
2. 伺服器可以執行額外的客戶端身份認證,例如,返回401狀態碼與描述在[RFC2616]中的相關|WWW-Authenticate|頭欄位。
3. 伺服器可以使用3xx狀態碼[RFC2616]重定向客戶端。注意,此步驟可能連同,之前,或之後的可選的上面描述的身份驗證步驟一起發生。
4. 建立如下資訊:
/origin/
客戶端握手中的|Origin|頭欄位表示建立連接的腳本的來源。Origin是序列化為ASCII並轉換為小寫。伺服器可以使用這個資訊作為決定是否接受傳入連接的一部分。如果伺服器沒有驗證origin,它將接受來自任何地方的連接。如果伺服器不想接受這個連接,它必須返回一個適當的HTTP錯誤碼(例如,403 Forbidden)並中斷描述在本章中的WebSocket握手。更多的詳細資訊,請參閱第10章。
/key/
在客戶端握手中的|Sec-WebSocket-Key|頭欄位包括一個base64編碼值,如果解碼,長度是16位元組。這個(編碼的)值用在創建伺服器握手時來表示接受連接。伺服器沒必要使
/version/
客戶端手中的|Sec-WebSocket-Version|頭欄位包括客戶端試圖通訊的WebSocket協議的版本。如果該版本沒有匹配伺服器理解的一個版本,伺服器必須中斷描述在本節的WebSocket握手並替代返回一個適當的HTTP錯誤碼(例如,426 Upgrade Required)且一個|Sec-WebSocket-Version|頭欄位表示伺服器能理解的版本。
/resource name/
由伺服器提供的服務的標識符。如果伺服器提供多個服務,那麼該值應該源自客戶端手中的GET方法的”Request-URI”中給定的資源名。如果請求的伺服器不可用,伺服器必須發生一個適當的HTTP錯誤碼(例如404 NotFound)並中斷WebSocket握手。
/subprotocol/
或者一個代表伺服器準備使用的子協議的單個值或者null。選擇的值必須源自客戶端握手,從|Sec-WebSocket-Protocol|欄位具體選擇一個值,伺服器將使用它用於這個連接(如果有)。如果客戶端握手不包含這樣一個頭欄位或者如果伺服器不同意任何客戶端請求的子協議,僅接受的值為null。這個欄位不存在等價於null值(意思是如果伺服器不想同意任何建議的子協議,它必須在它的響應中不發送回一個|Sec-WebSocket-Protocol|頭欄位)。用於這些目的,空字元串與null值是不一樣的,且它不是這個欄位合法的值。用於該頭欄位的值ABNF是(符號),構造定義和規則在[RFC2616]中給出。
/extensions/
表示伺服器準備使用的協議級別擴展的一個列表(可能為空)。如果伺服器支援多個擴展,那麼該值必須源自客戶端握手,通過從|Sec-WebSocket-Extensions|欄位具體地選擇一個或多個值。這個欄位不存在等價於null值。用於這些目的,空字元串與null值是不一樣的。客戶未列出的擴展必須不被列出。那些值應該被選擇和解釋的方法在9.1節討論。
5. 如果伺服器選擇接受傳入的連接,它必須以一個有效的表示以下的HTTP響應應答。
5.1一個按照RFC 2616 [RFC2616]帶有101響應嗎的Status-Line。這樣的響應可能看起來像”HTTP/1.1 101 Switching Protocols”。
5.2 一個按照RFC 2616 [RFC2616]帶有”websocket”的 |Upgrade|頭欄位。
5.3 一個帶有”Upgrade”的|Connection|頭欄位。
5.4 一個|Sec-WebSocket-Accept|頭欄位,該頭欄位的值通過連接/key/構造,它定義在4.2.2節第4步,帶有字元串”258EAFA5-E914-47DA-95CA-C5AB0DC85B11″,採用SHA-1散列這個連接的值來獲取一個20位元組的值並base64編碼(參考[RFC4648]第4章)這個20位元組的散列。
該頭欄位的ABNF [RFC2616]定義如下:
Sec-WebSocket-Accept = base64-value-non-empty
base64-value-non-empty = (1*base64-data [ base64-padding ]) |
base64-padding
base64-data = 4base64-character
base64-padding = (2base64-character “==”) |
(3base64-character “=”)
base64-character = ALPHA | DIGIT | “+” | “/”
注意:例如,如果客戶端握手中的|Sec-WebSocket-Key|頭欄位的值是”dGhlIHNhbXBsZSBub25jZQ==”,伺服器將追加字元串”258EAFA5-E914-47DA-95CA-C5AB0DC85B11″為字元串”dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-
C5AB0DC85B11″形式。伺服器將採取SHA-1散列這個字元串,並給出值0xb3 0x7a 0x4f 0x2c 0xc0 0x62 0x4f 0x16 0x90 0xf6 0x46 0x06 0xcf 0x38 0x59 0x45 0xb2 0xbe 0xc4 0xea。這個值接著base64編碼,給出值”s3pPLMBiTxaQ9kYGzzhZRbK+xOo=”,這將在|Sec-WebSocket-Accept|頭欄位中被返回。
5.5 可選的,一個|Sec-WebSocket-Protocol|頭欄位,帶有一個定義在4.2.2節第4不的值/subprotocol/。
5.6可選的,一個|Sec-WebSocket-Extensions|頭欄位,帶有一個定義在4.2.2節第4步的值/extensions/。如果使用多個擴展,那麼可以把所有都列在一個|Sec-WebSocket-Extensions|頭欄位中或者分配到|Sec-WebSocket-Extensions|頭欄位的多個實例之間。
這就完成了伺服器握手。如果伺服器完成這些步驟且沒有中斷WebSocket握手,伺服器認為WebSocket連接已建立且WebSocket連接處於OPEN狀態。此時,伺服器可以開始發送(和接收)數據了。
4.3 為握手中使用的新的頭欄位整理的ABNF
本節中使用的ABNF語法/規範來自[RFC2616]第2.1節,包括「隱式*LWS規則」。
注意:以下ABNF約定於本節中。一些規則的名相當於相應的頭欄位名字。這樣的規則表示相應的頭欄位值,例如Sec-WebSocket-Key ABNF規則描述了 |Sec-WebSocket-Key|頭欄位值的語法。在名字中帶有”-Client”後綴的ABNF規則僅用在由客戶端到伺服器端發送請求的情況:在名字中帶有”-Server”後綴的ABNF規則僅用在由伺服器到客戶端發生響應的情況。例如,ABNF規則Sec-WebSocket-Protocol-Client描述了客戶端到伺服器發送的|Sec-WebSocket-Protocol|頭欄位值的語法。
以下新的頭欄位可以在從客戶端到伺服器握手期間被發送:
Sec-WebSocket-Key = base64-value-non-empty
Sec-WebSocket-Extensions = extension-list
Sec-WebSocket-Protocol-Client = 1#token
Sec-WebSocket-Version-Client = version
base64-value-non-empty = (1*base64-data [ base64-padding ]) |
base64-padding
base64-data = 4base64-character
base64-padding = (2base64-character “==”) |
(3base64-character “=”)
base64-character = ALPHA | DIGIT | “+” | “/”
extension-list = 1#extension
extension = extension-token *( “;” extension-param )
extension-token = registered-token
registered-token = token
extension-param = token [ “=” (token | quoted-string) ]
; When using the quoted-string syntax variant, the value
; after quoted-string unescaping MUST conform to the
; ‘token’ ABNF.
NZDIGIT = “1” | “2” | “3” | “4” | “5” | “6” |
“7” | “8” | “9”
version = DIGIT | (NZDIGIT DIGIT) |
(“1” DIGIT DIGIT) | (“2” DIGIT DIGIT)
; Limited to 0-255 range, with no leading zeros
以下新的頭欄位可以在伺服器到客戶端握手期間被發送:
Sec-WebSocket-Extensions = extension-list
Sec-WebSocket-Accept = base64-value-non-empty
Sec-WebSocket-Protocol-Server = token
Sec-WebSocket-Version-Server = 1#version
4.4 支援多個版本的WebSocket協議
本節提供了在客戶端和伺服器中支援多個版本的WebSocket協議的一些指導。
使用WebSocket版本通知能力(|Sec-WebSocket-Version|頭欄位),客戶端可以初始請求它選擇的WebSocket協議的版本(這並不一定必須是客戶端支援的最新的)。如果伺服器支援請求的版本且握手消息是本來有效的,伺服器將接受該版本。如果伺服器不支援請求的版本,它必須以一個包含所有它將使用的版本的|Sec-WebSocket-Version|頭欄位(或多個|Sec-WebSocket-Version|頭欄位)來響應。此時,如果客戶端支援一個通知的版本,它可以使用新的版本值重做WebSocket握手。
以下示例演示了上述的版本協商:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
…
Sec-WebSocket-Version: 25
伺服器的響應可能看起來像如下:
HTTP/1.1 400 Bad Request
…
Sec-WebSocket-Version: 13, 8, 7
注意伺服器最後的響應可能也看起來像:
HTTP/1.1 400 Bad Request
…
Sec-WebSocket-Version: 13
Sec-WebSocket-Version: 8, 7
客戶端現在可以重做符合版本13的握手:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
…
Sec-WebSocket-Version: 13
5 數據幀
5.1 概述
在WebSocket協議中,數據使用幀序列來傳輸。為避免混淆網路中間件(例如攔截代理)和出於安全原因,第10.3節進一步討論,客戶端必須掩碼它發送到伺服器的所有幀(更多詳細資訊請參見5.3節)。(注意不管WebSocket協議是否允許在TLS之上,掩碼都要做。)當收到一個沒有掩碼的幀時,伺服器必須關閉連接。在這種情況下,伺服器可能發送一個定義在7.4.1節的狀態碼1002(協議錯誤)的Close幀。伺服器必須不掩碼發送到客戶端的所有幀。如果客戶端檢測到掩碼的幀,它必須關閉連接。在這種情況下,它可能使用定義在7.4.1節的狀態碼1002(協議錯誤)。(這些規則可能在未來規範中放寬。)
基本幀協議定義了帶有操作碼的幀類型、負載長度、和用於「擴展數據」與「應用數據」以及它們一起定義的「負載數據」的指定位置。某些位元組和操作碼保留用於未來協議的擴展。
一個數據幀可以被客戶端或者伺服器在打開階段握手完成之後和端點發送Close幀之前的任何時候傳輸(5.5.1節)。
5.2 基本幀協議
用於數據傳輸部分的報文格式是通過本節中詳細描述的ABNF來描述。(注意,不像本文檔的其他章節,本節中的ABNF是在位bit組上的操作。每一個位組的長度在注釋中指出。在編碼報文時,最重要的位是在ABNF的最左邊。)下圖給出了幀的高層次概述。在下圖和本節後邊指定的ABNF之間衝突的,這個圖表時權威的。
0 1 2 3 0 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 ... | +---------------------------------------------------------------+
FIN: 1 bit
指示這個是消息的最後片段。第一個片段可能也是最後的片段。
RSV1, RSV2, RSV3: 1 bit each
必須是0,除非一個擴展協商為非零值定義含義。如果收到一個非零值且沒有協商的擴展定義這個非零值的含義,接收端點必須_失敗WebSocket連接_。
Opcode: 4 bits
定義了一個「負載數據」的解釋。如果收到一個未知的操作碼,接收端點必須_失敗WebSocket連接_。定義了以下值:
* %x0 代表一個繼續幀
* %x0 denotes a continuation frame
* %x1 代表一個文本幀
* %x1 denotes a text frame
* %x2 代表一個二進位幀
* %x2 denotes a binary frame
* %x3-7 保留用於未來的非控制幀
* %x3-7 are reserved for further non-control frames
* %x8 代表連接關閉
* %x8 denotes a connection close
* %x9 代表ping
* %x9 denotes a ping
* %xA 代表pong
* %xA denotes a pong
* %xB-F 保留用於未來的控制幀
* %xB-F are reserved for further control frames
Mask: 1 bit
定義是否「負載數據」是掩碼的。如果設置為1,一個掩碼鍵出現在masking-key,且這個是用於根據5.3節解掩碼(unmask)「負載數據」。從客戶端發送到伺服器的所有幀有這個位置為1。
Payload length: 7 bits, 7+16 bits, 或者 7+64 bits
「負載數據」的長度,以位元組為單位:如果0-125,這是負載長度。如果126,之後的兩位元組解釋為一個16位的無符號整數是負載長度。如果127,之後的8位元組解釋為一個64位的無符號整數(最高有效位必須是0)是負載長度。多位元組長度數量以網路位元組順序來表示。注意,在所有的情況下,最小數量的位元組必須用於編碼長度,例如,一個124位元組長的字元串的長度不能被編碼為序列126,0,124.負載長度是「擴展數據」長度+「應用數據」長度。「擴展數據」長度可能是零,在這種情況下,負載長度是「應用數據」長度。
Masking-key: 0 or 4 bytes
客戶端發送到伺服器的所有幀通過一個包含在幀中的32位值來掩碼。如果mask位設置為1,則該欄位存在,如果mask位設置為0,則該欄位缺失。詳細資訊請參見5.3節 客戶端到伺服器掩碼。
Payload data: (x+y) bytes
「負載數據」定義為「擴展數據」連接「應用數據」。
Extension data: x bytes
「擴展數據」是0位元組除非已經協商了一個擴展。任何擴展必須指定「擴展數據」的長度,或長度是如何計算的,以及擴展如何使用必須在打開階段握手期間協商。如果存在,「擴展數據」包含在總負載長度中。
Application data: y bytes
任意的「應用數據」,佔用「擴展數據」之後幀的剩餘部分。「應用數據」的長度等於負載長度減去「擴展數據」長度。
基本幀協議是由以下ABNF[RFC5234]正式定義的。重要的是要注意這個數據是二進位表示的,而不是ASCII字元。因此,一個1位長度的欄位取值為%x0 / %x1是表示為單個為,其值為0或1,不是以ASCII編碼代表字元「0」或「1」的完整位元組(8位位組)。4位長度的欄位值介於%x0-F之間,是通過4位表示的,不是通過ASCI字元或者這些值的完整位元組(8位位組)。[RFC5234]沒有指定字元編碼:「規則解析為最終值的字元串,有時候被稱為字元。在ABNF中,一個字元僅僅是一個非負整數。在某些上下文中,一個值到一個字符集的特定映射(編碼)將被指定。」在這裡,指定的編碼是二進位編碼,每一個最終值是編碼到指定數量的比特中,每個欄位是不同的。
ws-frame = frame-fin ; 1 bit in length
frame-rsv1 ; 1 bit in length
frame-rsv2 ; 1 bit in length
frame-rsv3 ; 1 bit in length
frame-opcode ; 4 bits in length
frame-masked ; 1 bit in length
frame-payload-length ; either 7, 7+16, or 7+64 bits in length
[ frame-masking-key ] ; 32 bits in length
frame-payload-data ; n*8 bits in length, where n >= 0
frame-fin = %x0 ; more frames of this message follow
/ %x1 ; final frame of this message
; 1 bit in length
frame-rsv1 = %x0 / %x1
; 1 bit in length, MUST be 0 unless
; negotiated otherwise
frame-rsv2 = %x0 / %x1
; 1 bit in length, MUST be 0 unless
; negotiated otherwise
frame-rsv3 = %x0 / %x1
; 1 bit in length, MUST be 0 unless
; negotiated otherwise
frame-opcode = frame-opcode-non-control /
frame-opcode-control /
frame-opcode-cont
frame-opcode-cont = %x0 ; frame continuation
frame-opcode-non-control= %x1 ; text frame
/ %x2 ; binary frame
/ %x3-7
; 4 bits in length,
; reserved for further non-control frames
frame-opcode-control = %x8 ; connection close
/ %x9 ; ping
/ %xA ; pong
/ %xB-F ; reserved for further control
; frames
; 4 bits in length
frame-masked = %x0
; frame is not masked, no frame-masking-key
/ %x1
; frame is masked, frame-masking-key present
; 1 bit in length
frame-payload-length = ( %x00-7D )
/ ( %x7E frame-payload-length-16 )
/ ( %x7F frame-payload-length-63 )
; 7, 7+16, or 7+64 bits in length,
; respectively
frame-payload-length-16 = %x0000-FFFF ; 16 bits in length
frame-payload-length-63 = %x0000000000000000-7FFFFFFFFFFFFFFF
; 64 bits in length
frame-masking-key = 4( %x00-FF )
; present only if frame-masked is 1
; 32 bits in length
frame-payload-data = (frame-masked-extension-data
frame-masked-application-data)
; when frame-masked is 1
/ (frame-unmasked-extension-data
frame-unmasked-application-data)
; when frame-masked is 0
frame-masked-extension-data = *( %x00-FF )
; reserved for future extensibility
; n*8 bits in length, where n >= 0
frame-masked-application-data = *( %x00-FF )
; n*8 bits in length, where n >= 0
frame-unmasked-extension-data = *( %x00-FF )
; reserved for future extensibility
; n*8 bits in length, where n >= 0
frame-unmasked-application-data = *( %x00-FF )
; n*8 bits in length, where n >= 0
5.3 客戶端到伺服器掩碼
一個掩碼的幀必須有5.2節定義的欄位frame-masked設置為1。
掩碼鍵完全包含在幀中,5.2節定義的frame-masking-key。它用於掩碼定義在相同章節的frame-payload-data中的「負載數據」,其包含「擴展數據」和「應用數據」。
掩碼鍵是由客戶端隨機選擇的32位值。當準備一個掩碼的幀時,客戶端必須從允許的32位值集合中選擇一個新的掩碼鍵。掩碼鍵需要是不可預測的;因此,掩碼鍵必須來自一個強大的熵源,且用於給定幀的掩碼鍵必須不容易被伺服器/代理預測用於後續幀的掩碼鍵。掩碼鍵的不可預測性對防止惡意應用的作者選擇出現在報文上的位元組是必要的。RFC 4086 [RFC4086]討論了什麼需要一個用於安全敏感應用的合適熵源。
掩碼不影響「負載數據」的長度。變換掩碼數據到解掩碼數據,或反之亦然,以下演算法被應用。相同的演算法應用,不管轉化的方向,例如,相同的步驟即應用到掩碼數據也應用到解掩碼數據。
變換數據的八位位組i是原始數據的八位位組異或i取模4位置的掩碼鍵的八位位組:
j = I MOD 4
transformed-octet-I = original-octet-I XOR masking-key-octet-j
負載長度,在幀中以frame-payload-length表示,不包括掩碼鍵的長度。它是「負載數據」的長度,例如,跟在掩碼鍵後邊的位元組數。
5.4 分片
分片的主要目的是運行當消息開始但不必緩衝該消息時發送一個未知大小的消息。如果消息不能被分片,那麼端點將不得不緩衝整個消息以便在首位元組發生之前統計出它的長度。對於分片,伺服器或中間件可以選擇一個合適大小的緩衝,當緩衝滿時,寫一個片段到網路。
第二個分片的用例是用於多路復用,一個邏輯通道上的一個大消息獨佔輸出通道是不可取的,因此多路復用需要可以分割消息為更小的分段來更好的共享輸出通道。(注意,多路復用擴展在本文檔中沒有描述。)
除非另有擴展規定,幀沒有語義含義。一個中間件可能合併並且/或分割幀,如果客戶端和伺服器沒有協商擴展;或如果已協商了一些擴展,但中間件理解所有協商的擴展且知道如何去合併且/或分割在這些擴展中存在的幀。這方面的一個含義是,在沒有擴展的情況下,發送者和接收者必須不依賴於特定幀邊界的存在。
以下規則應用到分片:
1、一個沒有分片的消息由單個帶有FIN位設置(5.2節)和一個非0操作碼的幀組成。
2、一個分片的消息是單個帶有FIN為清零(5.2節)和一個非0操作碼的幀組成,跟隨零個或多個帶有FIN為清零和操作碼設置為0的幀,且終止於一個帶有FIN為設置且0操作碼的幀。一個分片的消息概念上是等價於單個大的消息。其複雜是等價於按順序串聯片段的負載;然而,在存在擴展的情況下,這個可能不適用擴展定義的「擴展數據」存在的解釋。例如:「擴展數據」可能僅在首個片段開始處存在且應用到隨後的片段,或「擴展數據」可以存在於僅用於到特定片段的每個片段。在沒有「擴展數據」的情況下,以下例子展示了分片如何工作。
例子:對於一個作為三個片段發送的文本消息,第一個片段將有一個0x1操作碼和一個FIN位清零,第二個片段將有一個0x0操作碼和一個FIN位清零,且第三個片段將有0x0操作碼和一個FIN位設置。
3、控制幀(參見5.5節)可能被注入到一個分片消息的中間。控制幀本身必須不被分割。
4、消息分片必須按發送者發送順序交付給收件人。
5、片段中的一個消息必須不能與片段中的另一個消息交替,除非已協商了一個能解釋交替的擴展。
6、一個端點必須能處理一個分片消息找那個間的控制幀。
7、一個發送者可以為非控制消息創建任何大小的片段。
8、 客戶端和伺服器必須支援接收分片和非分片的消息。
9、由於控制幀不能被分片,一個中間件必須不嘗試改變控制幀的分片。
10、如果使用了任何保留的位置且這些值的意思對中間件是未知的,一個中間件必須不改變一個消息的分片。
11、在一個連接的上下文中,已經協商了擴展且中間件不知道協商的擴展的語義,一個中間件必須不改變任何消息的分片。同樣,沒有看見WebSocket握手(且沒有被通知有關它的內容)、導致一個WebSocket連接的一個中間件,必須不改變這個連接的任何消息的分片。
12、由於這些規則,一個消息的所有分片是相同類型的,以第一個片段的操作碼設置。因為控制幀不能被分片,用於一個消息中的所有分片的類型必須是文本、或者二進位、或者一個保留的操作碼。
注意:如果控制幀不能被插入,一個ping延遲,例如,如果跟著一個大消息將是非常長的。因此,要求在分片消息的中間件處理控制幀。
實現注意:在沒有任何擴展時,一個接收者不必按順序緩衝整個幀來處理它,例如,如果使用了一個流式API,一個幀的一部分能被交付到應用。但是,請注意這個假設可能不適用所有未來的WebSocket擴展。
5.5 控制幀
控制幀由操作碼確定,其中操作碼最重要的位是1.當前定義的用於控制幀的操作碼包括0x8 (Close), 0x9 (Ping),和0xA (Pong)。操作碼0xB-0xF保留用於未來尚未定義的控制幀。
控制幀用於傳達有關WebSocket的狀態。控制幀可以插入到分片消息的中間。
所有的控制幀必須有一個125位元組的負載長度或者更少,必須不被分段。
5.5.1 關閉
關閉幀包含0x8操作碼。
關閉幀可以包含內容體(幀的「應用數據」部分)指示一個關閉的原因,例如端點關閉了、端點收到的幀太大、或端點收到的幀不符合端點期望的格式。如果有內容體,內容體的頭兩個位元組必須是2位元組的無符號整數(按網路位元組順序)代表一個在7.4節的/code/值定義的狀態碼。跟蹤2位元組的整數,內容體可以包含UTF-8編碼的/reason/值,本規範沒有定義它的解釋。數據不必是人類可讀的但可能對調試或傳遞打開連接的腳本相關的資訊是有用的。由於數據不保證人類可讀,客戶端必須不把它顯示給最終的用戶。
客戶端發送到伺服器的關閉幀必須根據5.3節被掩碼。
在應用發送關閉幀之後,必須不發送任何更多的數據幀。
如果一個端點接收到一個關閉幀且先前沒有發送一個關閉幀,端點必須在響應中發送一個關閉幀。(當在響應中發生關閉幀時,端點通常回送它接收到的狀態碼)它應該根據實際情況儘快這樣做。端點可以延遲發送關閉幀直到它當前消息發送了(例如,如果一個分片消息的大多數已經發送了,端點可以發送剩餘的片段在發送一個關閉幀之前)。但是,不保證一個已經發送關閉幀的端點將繼續處理數據。
發送並接收一個關閉消息後,一個端點認為WebSocket連接關閉了且必須關閉底層的TCP連接。伺服器必須立即關閉底層TCP連接,客戶端應該等待伺服器關閉連接但可能在發送和接收一個關閉消息之後的任何時候關閉連接,例如,如果它沒有在一個合理的時間周期內接收到伺服器的TCP關閉。
如果客戶端和伺服器同時都發送了一條關閉消息,兩個端點都將發送和接收一個關閉消息且應該認為WebSocket連接關閉了並關閉底層TCP連接。
5.5.2 Ping
Ping幀包含0x9操作碼。
Ping幀可以包含「應用數據」。
當接收到一個ping幀時,一個端點必須在響應中發送一個Pong幀,除非它早已接收到一個關閉幀。它應該儘可能快地以Pong幀響應。Pong幀在5.5.3節討論。
一個端點可以在連接建立之後並在連接關閉之前的任何時候發送一個Ping幀。
注意:一個Ping可以充當一個keepalive,也可以作為驗證遠程端點仍可響應的手段。
5.5.3 Pong
Pong幀包含一個0xA操作碼。
5.5.2節詳細說明了應用Ping和Pong幀的要求。
一個Pong幀在響應中發送到一個Ping幀必須有在將恢復的Ping幀的消息內容體中發現相同的「應用數據」。
如果端點接收到一個Ping幀且尚未在響應找那個發送Pong幀到之前的Ping幀,端點可以選擇僅為最近處理的Ping幀發送一個Pong幀。
一個Pong幀可以未經允許請求的發送。這個充當單向的心跳。到未經請求的Pong幀的一個響應式不期望的。
5.6 數據幀
數據幀(例如,非控制幀)由操作碼最高位是0的操作碼標識。當前為數據幀定義的操作碼包括0x1 (文本), 0x2 (二進位)。 操作碼 0x3-0x7保留用於未來尚未定義的非控制幀。
數據幀攜帶應用層和/或擴展層數據。操作碼決定了數據的解釋:
Text
「負載數據」是編碼為UTF-8的文本數據。注意,一個特定的文本幀可能包括部分UTF-8序列;不管怎麼樣,整個消息必須包含有效的UTF-8。重新組裝的消息中無效的UTF-8的處理描述在8.1節。
Binary
「負載數據」是隨意的二進位數據,其解釋僅僅是在應用層。
5.7 示例
1、未掩碼文件消息的單個幀
* 0x81 0x05 0x48 0x65 0x6c 0x6c 0x6f (contains “Hello”)
2、掩碼的文本消息的單個幀
* 0x81 0x85 0x37 0xfa 0x21 0x3d 0x7f 0x9f 0x4d 0x51 0x58
(contains “Hello”)
3、一個分片的未掩碼的文本消息
* 0x01 0x03 0x48 0x65 0x6c (contains “Hel”)
* 0x80 0x02 0x6c 0x6f (contains “lo”)
4、未掩碼的Ping請求和掩碼的Ping響應
* 0x89 0x05 0x48 0x65 0x6c 0x6c 0x6f (包含內容體「Hello」,但內容體的內容是隨意的)
* 0x8a 0x85 0x37 0xfa 0x21 0x3d 0x7f 0x9f 0x4d 0x51 0x58(包含內容體「Hello」,匹配ping的內容體)
5、單個未掩碼幀中的256位元組的二進位消息
* 0x82 0x7E 0x0100 [256位元組的二進位數據]
6、單個未掩碼幀中的64KB的二進位消息
* 0x82 0x7F 0x0000000000010000 [65536位元組的二進位數據]
5.8 可拓展性
協議被設計為允許擴展,這將增加功能到基礎協議。端點的一個連接必須在打開階段握手期間協商使用的任何擴展。本規範提供了用於擴展的操作碼0x3 到 0x7 和0xB 到 0xF、「擴展數據」欄位、和幀-rsv1、幀-rsv2、和幀-rsv3幀頭位。9.1節進一步討論了擴展協商。以下是一些預期使用的擴展。這個列表是不完整的也是不規範的。
1、「擴展數據」可以放置在「負載數據」中的「應用數據」之前。
2、保留的位可以分配給需要的每一個幀。
3、保留的操作碼值能被定義。
4、如果需要更多的操作碼值,保留的位可以分配給操作碼欄位。
5、一個保留的位或一個「擴展」操作碼可以定義以從「負載數據」中分配額外的位來定義更大的操作碼或更多的每位幀位。
6 發送和接收數據
6.1 發送數據
為了_發送一個WebSocket消息_,其中包括WebSocket連接之上的/data/,端點必須執行以下步驟。
1. 端點必須確保WebSocket連接處於OPEN狀態(比較4.1節和4.2.2節)。如果在任何時候WebSocket連接的狀態改變了,端點必須終止以下步驟。
2. 端點必須封裝/data/到定義在5.2節的一個WebSocket幀。如果要發送的數據太大或如果在端點想要開始發生數據時數據作為一個整體不可用,端點可以按照5.2節的定義交替地封裝數據到一系列的幀中。
3. 第一個包含數據的幀的操作碼(幀-opcode)必須按照5.2節的定義被設置為適當的值用於接收者解釋數據是文本還是二進位數據。
4. 包含數據的最後幀的FIN位(幀-FIN)必須按照5.2節的定義設置為1。
5. 如果數據正由客戶端發送,幀必須按照5.3節的定義被掩碼。
6. 如果任何擴展(第9章)已經協商用於WebSocket連接,額外的考慮可以按照這些擴展定義來應用。
7. 已成型的幀必須在底層網路連接之上傳輸。
6.2 接收數據
為了接收WebSocket數據,端點監聽底層網路連接。傳入數據必須按照5.2節的定義解析為WebSocket幀。如果接收到一個控制幀(5.5節),幀必須按照5.5節的定義來處理。當接收到一個數據幀時(5.6節),端點必須注意5.2節由操作碼(幀-opcode)定義的數據的/type/。這個真的「應用數據」被定義為消息的/data/。如果幀由一個未分片的消息組成(5.4節),這是說_已經接收到一個WebSocket消息_,其類型為/type/且數據為/data /。如果幀時一個分片消息的一部分,隨後數據幀的「應用數據」連接在一起形成/data /.當接收到由FIN位(幀-fin)指示的最後片段時,這是說_已經接收到一個WebSocket消息_,其數據為/data/(由連續片段的「應用數據」組成)且其類型為/type/(分配消息的第一個幀指出)。隨後的數據幀必須被解釋為屬於一個新的WebSocket消息。
擴展(第9章)可以改變數據如何讀的語義,尤其包括什麼組成一個消息的邊界。擴展,除了在負載中的「應用數據」之前添加「擴展數據」外,也可以修改「應用數據」(例如壓縮它)。
伺服器必須按照5.3節的定義為從客戶端接收到的數據幀移除掩碼。
7 關閉連接
7.1 定義
7.1.1 關閉WebSocket連接
為_關閉WebSocket_連接_,端點需關閉底層TCP連接。端點應該使用一個方法完全地關閉TCP連接,以及TLS會話,如果合適,丟棄任何可能已經接收的尾隨的位元組。當必要時端點可以通過任何可用的手段關閉連接,例如當受到攻擊時。
底層TCP連接,在大多數正常情況下,應該首先被伺服器關閉,所以它持有TIME_WAIT狀態而不是客戶端(因為這會防止它在2個報文最大生存時間(2MSL)內重新打開連接,然而當一個新的帶有更高的seq number的SYN時沒有對應的伺服器影響TIME_WAIT連接被立即重新打開)。在異常情況下(例如在一個合理的時間量後沒有接收到伺服器的TCP Close)客戶端可以發起TCP Close。因此,當伺服器被指示_關閉WebSocekt連接_,它應該立即發起一個TCP Close,且當客戶端被指示時也這麼做時,它應該等待伺服器的一個TCP Close。
例如一個如何使用Berkeley sockets在C中得到完全地關閉的例子,一端會在socket上以SHUT_WR調用shutdown(),調用recv()直到獲得一個指示那個節點也已經執行了一個有序關閉的0返回值,且最終在socket上調用close()方法。
7.1.2 啟動WebSocket關閉階段握手
為了_啟動WebSocket關閉階段握手_,其帶有一個狀態碼(7.4節)/code/和一個可選的關閉原因(7.1.6節)/reason/,一個端點必須按照5.5.1節的描述發送一個Close控制幀,其狀態碼設置為/code/且其關閉原因設置為/reason/。一旦一個端點已經發送並接收到一個Close控制幀,哪個端點應該按照7.1.1節的描述_關閉WebSocket連接_。
7.1.3 WebSocket關閉階段握手已啟動
一旦發送或接收到一個Close控制幀,這就是說,_WebSocket關閉階段握手已啟動_,且WebSocket連接處於CLOSING狀態。
7.1.4 WebSocket已關閉
當底層TCP連接已關閉,這就是說_WebSocket連接已關閉_且WebSocket連接處於CLOSED狀態。如果TCP連接在WebSocket關閉階段我是已經完成後被關閉,WebSocket連接被說成已經_完全地_關閉了。
如果WebSocket連接不能被建立,這就是說,_ WebSocket 連接關閉了_,但不是_完全的_。
7.1.5 WebSocket連接關閉程式碼
按照5.5.1 和 7.4節的定義,一個Close控制幀可以包含一個表示關閉原因的狀態碼。一個正關閉的WebSocket連接可以同時由兩個端點初始化。_WebSocket連接Close Code_定義為包含在由實現該協議的應用接收到的第一個Close控制幀的狀態碼(7.4節)。如果這個Close控制幀不包含狀態碼,_WebSocket連接Close Code_被認為是1005。如果_WebSocket連接已關閉_且端點沒有接收到Close狀態碼(例如可能發生在底層傳輸連接丟失時),_WebSocket連接Close Code_被認為是1006。
注意:兩個端點可以有不一致的_WebSocket連接關閉程式碼_。例如,如果遠程端點發送了一Close幀,但本地應用還沒有從它的socket接收緩衝區中讀到包含Close幀的數據,且本地應用獨立地決定關閉連接和發送一個Close幀,兩個端點都將發送和接收Close幀且將不發送更多的Close幀。每一個端點將看見另一端發送的以_WebSocket連接關閉程式碼_結束的狀態碼。例如,在兩個端點獨立且在大致相同的時間同時_開啟WebSocket關閉階段握手_的情況下,兩個端點可以有不一致的_WebSocket連接關閉程式碼_是可能的。
7.1.6 WebSocket連接關閉原因
按照5.5.1 和 7.4節的定義,一個Close控制幀可以包含一個指示關閉原因的狀態碼,接著是UTF-8編碼的數據,上述數據留給斷點解釋且本協議沒有定義。WebSocket連接的關閉可以被任何一個端點初始化,可能同時發生。_WebSocket連接關閉原因_由跟在包含在實現該協議的應用接收到的第一個Close控制幀狀態碼(7.4節)後邊的UTF-8編碼的數據定義。如果Close控制幀中沒有這樣的數據,_WebSocket連接關閉原因_是空字元串。
注意:按照7.1.5節指出的相同的邏輯,兩個端點可以有不一致的_WebSocket連接關閉原因_。
7.1.7 失敗WebSocket連接
某些演算法和規範要求端點_失敗WebSocket 連接_。要做到這一點,客戶端必須_關閉WebSocket 連接_,並可以以適當的方式把問題報告給用戶(這將對開發人員非常有用的)。同樣的,為了做到這一點,伺服器必須_關閉WebSocket 連接_,並應該記錄下問題。
如果_已建立的WebSocket連接_在端點需要_失敗WebSocket 連接_之前,端點應該在處理_關閉WebSocket 連接_之前發送一個帶有適當狀態碼的Close幀(7.4節)。如果端點認為另一邊不太可能接收到並處理關閉幀可以省略發送一個關閉幀,因為錯誤的性質,導致WebSocket連接失敗擺在首要位置。端點必須在被指示為_失敗WebSocket 端點_之後不繼續嘗試處理來自遠程端點的數據(包括響應關閉幀)。
除了上邊指出的或由應用層指定的(例如,使用WebSocket API的腳本),客戶端應該關閉連接。
7.2 異常關閉
7.2.1 客戶端發起的關閉
某些演算法,尤其在打開階段握手期間,需要客戶端_失敗WebSocket 連接_。為了做到這一點,客戶端必須按照7.1.7節定義的那樣_失敗WebSocket 連接_。
如果在任何時候,底層的傳輸層連接意外丟失,客戶端必須_失敗WebSocket 連接_。
除了上邊指出的或由應用層指定的(例如,使用WebSocket API的腳本),客戶端應該關閉連接。
7.2.2 服務端發起的關閉
某些演算法需要或推薦服務端在打開階段握手期間_中斷WebSocket 連接_。為了做到這一點,服務端必須簡單地_關閉WebSocket 連接_(7.1.1節)。
7.2.3 從異常關閉中恢復
異常關閉可能由任何原因引起。這樣的關閉可能是一個瞬時錯誤導致的,在這種情況下重新連接可能導致一個好的連接和一個重新開始的正常操作。這樣的關閉也可能是一個非瞬時問題導致的,在這種情況下如果每個部署的客戶端遇到異常關閉並立即且持續地嘗試重新連接,服務端可能會因為大量的客戶端嘗試重新連接遇到的拒絕服務攻擊。這種情況的最終結果可能是服務不能及時恢復或恢復是更加困難。
為了避免這個,當客戶端遇到本節描述的異常關閉之後嘗試重新連接時,應該使用某種形式的補償。
第一個重新連接嘗試應該延遲一個隨機的時間量。這種隨機延遲的參數的選擇留給客戶端決定;一個可隨機選擇的值在0到5秒是一個合理的初始延遲,不過客戶端可以選擇不同的間隔由於其選擇一個延遲長度基於實現經驗和特定的應用。
第一次重新連接嘗試失敗,隨後的重新連接嘗試應該延遲遞增的時間量,使用的方法如截斷二進位指數退避演算法。
7.3 正常連接關閉
服務端在需要是可能關閉WebSocket連接。客戶端不能隨意關閉WebSocket連接。在這兩種情況下,端點通過如下過程_開始WebSocket 關閉握手_初始化一個關閉(7.1.2節)。
7.4 狀態碼
當關閉一個已經建立的連接(例如,當在打開階段握手已經完成後發送一個關閉幀),端點可以表明關閉的原因。由端點解釋這個原因,並且端點應該給這個原因採取動作,本規範是沒有定義的。本規範定義了一組預定義的狀態碼,並指定哪些範圍可以被擴展、框架和最終應用使用。狀態碼和任何相關的文本消息是關閉幀的可選的組件。
7.4.1 定義的狀態碼
當發送關閉幀時端點可以使用如下預定義的狀態碼。
1000:1000表示正常關閉,意思是建議的連接已經完成了。
1001:1001 表示端點「離開」,例如伺服器關閉或瀏覽器導航到其他頁面。
1002:1002 表示端點因為協議錯誤而終止連接。
1003:1003 表示端點由於它收到了不能接收的數據類型(例如,端點僅理解文本數據,但接收到了二進位消息)而終止連接。
1004:保留。可能在將來定義某具體的含義。
1005:1005 是一個保留值,且不能由端點在關閉控制幀中設置此狀態碼。它被指定用在期待一個用於表示沒有狀態碼是實際存在的狀態碼的應用中。
1006:1006 是一個保留值,且不能由端點在關閉控制幀中設置此狀態碼。它被指定用在期待一個用於表示連接異常關閉的狀態碼的應用中。
1007:1007 表示端點因為消息中接收到的數據是不符合消息類型而終止連接(比如,文本消息中存在非UTF-8 [RFC3629]數據)。
1008:1008 表示端點因為接收到的消息違反其策略而終止連接。這是一個當沒有其它合適狀態碼(例如1003或1009)或如果需要隱藏策略的具體細節時能被返回的通用狀態碼。
1009:1009 表示端點因接收到的消息對它的處理來說太大而終止連接。
1010:1010 表示端點(客戶端)因為它期望伺服器協商一個或多個擴展,但伺服器沒有在WebSocket握手響應消息中返回它們而終止連接。所需要的擴展列表應該出現在關閉幀的/reason/部分。注意,這個狀態碼不能被伺服器端使用,因為它可以使WebSocket握手失敗。
1011:1011 表示伺服器端因為遇到了一個不期望的情況使它無法滿足請求而終止連接。
1015:1015是一個保留值,且不能由端點在關閉幀中被設置為狀態碼。它被指定用在期待一個用於表示連接由於執行TLS握手失敗而關閉的狀態碼的應用中(比如,伺服器證書不能驗證)。
7.4.2 保留的狀態碼範圍
0-999:0-999 範圍內的狀態碼不被使用。
1000-2999:1000-2999 範圍內的狀態碼保留給本協議、其未來的修訂和一個永久的和現成的公共規範中指定的擴展的定義。
3000-3999:3000-3999 範圍內的狀態碼保留給庫、框架和應用使用。這些狀態碼直接向IANA註冊。本規範未定義這些狀態碼的解釋。
4000-4999:4000-4999 範圍內的狀態碼保留用於私有使用且因此不能被註冊。這些狀態碼可以被在WebSocket應用之間的先前的協議使用。本規範未定義這些狀態碼的解釋。
8 錯誤處理
8.1 處理UTF-8編碼數據的錯誤
當一個端點解析位元組流為UTF-8數據,但發現位元組流實際上不是一個有效的UTF-8流,那麼端點必須_失敗WebSocket連接_。這條規則則應用在打開握手期間和隨後的數據交換期間。
9 擴展
WebSocket客戶端可以請求本規範的擴展,且WebSocket伺服器可以接受一些或所有客戶端請求的擴展。伺服器不必響應不是客戶端請求的任何擴展。如果擴展參數包含在客戶端和伺服器之間的協商中,這些參數必須按照參數應用到的擴展規範來選擇。
WebSocket clients MAY request extensions to this specification, and
9.1 協商擴展
客戶端通過包含一個|Sec-WebSocket- Extensions|頭欄位請求擴展,其按照正常的HTTP頭欄位規則(參考[RFC2616],4.2節)並且頭欄位的值按照以下ABNF定義[RFC2616]。注意本章使用的ABNF語法/規則來源於[RFC2616]包括「隱式的*LWS規範」。如果客戶端或伺服器在協商階段接收到的值不符合下邊的ABNF,這種畸形數據的接收人必須立即_失敗WebSocket連接_。
Sec-WebSocket-Extensions = extension-list
extension-list = 1#extension
extension = extension-token *( “;” extension-param )
extension-token = registered-token
registered-token = token
extension-param = token [ “=” (token | quoted-string) ]
當使用引用字元串的語法變種是,引用字元串之後的值必須符合token’ ABNF.
注意,像其他HTTP頭欄位,這個頭欄位可以跨多個行分割或組合,因此,以下是等價的:
Sec-WebSocket-Extensions: foo
Sec-WebSocket-Extensions: bar; baz=2
完全等價於
Sec-WebSocket-Extensions: foo, bar; baz=2
所有使用的extension-token必須是一個registered token(參考11.4節)。任何給定擴展提供的參數必須被擴展定義。注意,客戶端只需要提供使用任何公布的擴展,除非伺服器表示它希望使用擴展,否則必須使用它們。
注意:擴展的的順序是重要的。在多個擴展間的相互作用可以定義在定義擴展的文檔中。在沒有這樣定義的情況下,解釋是它請求中的客戶端列出的頭欄位表示一個它希望使用的頭欄位的偏好,第一個列出的選項是最優選的。伺服器在響應中列出的擴展表示擴展是實際政治用於連接的擴展。擴展應該修改數據和/或組幀,數據的操作順序應該假定是與打開階段握手期間伺服器響應中列出的擴展順序是一樣的。
例如,如果有兩個擴展”foo” 和 “bar” ,且如果伺服器發送的頭欄位|Sec-WebSocket-Extensions|有值”foo, bar” ,那麼數據上的操作將變為 bar(foo(data)),是更改數據本身(如壓縮)或更改可能「堆疊」的組幀。
可接受的擴展頭欄位(注意:為了可讀性,將摺疊較長行)的非規範例子:
Sec-WebSocket-Extensions: deflate-stream
Sec-WebSocket-Extensions: mux; max-channels=4; flow-control,
deflate-stream
Sec-WebSocket-Extensions: private-extension
伺服器通過包含一個容納了一個或多個擴展的客戶端請求的|Sec-WebSocket-Extensions|頭欄位來接受一個或多個擴展。所有擴展參數的解釋,和什麼構成一個有效的
9.2 已知擴展
擴展提供了一種機制來實現選擇性加入的附加協議特性。本文檔沒有定義任何擴展。但實現可以使用單獨定義的擴展。
10 安全注意事項
本章描述了一些適用於WebSocket協議的安全注意事項。具體的安全注意事項在本章的子章節描述。
10.1 非瀏覽器客戶端
WebSocket協議防止惡意的JavaScript運行在一個受信任的應用內部,比如web瀏覽器,例如,通過檢查頭欄位|Origin|(見下文)。更多細節請參考1.6節。在一個更強大的客戶短的情況下,這樣的假設不成立。
雖然該協議的目的是被web頁面中的腳本使用,它也可以被主機直接使用。這樣的主機按照它們自己的行為行事,因此可以發送偽造的|Origin|頭欄位,騙過伺服器。因此伺服器應該小心,假設它們是直接與來自已知源的腳本通訊,且必須考慮到它們可能以非預期方式訪問。尤其,伺服器不應該相信任何輸入是有效的。
例如:如果伺服器使用輸入作為SQL查詢的一部分,所有輸入文本在傳輸到SQL伺服器之前應該被轉義,以免伺服器收到SQl注入攻擊。
10.2 Origin注意事項
伺服器不打算處理來自任意web頁面的輸入,但僅限於網站應該驗證|Origin|頭是一個它們盼望的源。如果源指示時伺服器不可接受的,那麼它應該以一個包含HTTP 403 Forbidden的狀態碼的回復響應WebSocket握手。
當不受信任方通常是一個執行在受信任的客戶端上下文中的JavaScript應用的作者時,|Origin|頭欄位可以保護攻擊的情況。客戶端本身可以與伺服器聯繫,並通過|Origin|頭欄位機制,決定是否提供JavaScript應用的這些通訊許可權。目的不是為了阻止非瀏覽器建立連接,而是確保受信的瀏覽器在潛在的惡意JavaScript控制下不能偽造WebSocket握手。
10.3 攻擊基礎設施(掩碼)
除了端點是WebSockets攻擊的目標之外,web基礎設施的其他部分,如代理,也可能是攻擊的對象。在本協議正在開發時,進行了一個實驗旨在演示一類代理上的攻擊,其導致部署在野的快取代理中毒。一般的攻擊形式是在「攻擊者」的控制下建立一個到伺服器的連接,執行類似於WebSockets協議建立連接的HTTP連接上的UPGRADE,且隨後在已經UPGRADE的連接上發送數據,看起來像一個GET請求一個特定的已知資源(在一次攻擊中,很可能會像廣泛部署的用於跟蹤點擊或一個廣告服務網路資源的腳本)。遠程伺服器響應的東西看起來像是到偽造的GET請求的一個響應,且這個響應將被一個非零百分比的部署的中間件快取,因此使快取中了毒了。這種攻擊的靜效應將是如果說服用戶去訪問攻擊者控制的網站,攻擊者可能使用戶和其他晚於相同快取的用戶的快取中毒且在其他源運行惡意腳本,影響網路安全模型。
為避免這種部署的中間件上的攻擊,前置不兼容HTTP的和幀一起的應用提供的數據是不夠的,因為無法詳盡地發現和測試每一個不符合中間件的不跳過這樣的非HTTP幀和錯誤地假裝幀負載。因此,採用的防禦是掩碼所有從客戶端到服務端發送的數據,使遠程腳本(攻擊者)無法控制數據如何在電線上發送,從而無法構造一個可能被中間件誤解釋的作為一個HTTP請求的消息。
客戶端必須為每一幀選擇一個新的掩碼密鑰,使用一個不能被提供數據的終端應用預測。例如,每次掩碼可以從一個強加密的隨機數生成器獲取。如果使用相同的密鑰或存在一個可預測的模式用於選擇下一個密鑰,當掩碼後,攻擊者可以發送一個消息,可能作為一個HTTP請求出現(通過交換消息,攻擊者希望觀察線上並用下一個將被使用的掩碼密鑰掩碼它,當客戶端應用它時掩碼密鑰將有效的解碼數據)。
一旦從客戶端傳輸一個幀已經開始,幀的負載(應用提供的數據)必須不能被應用修改也是必要的。
否則,攻擊者可能發送一個已知初始數據(如都是0)的長幀,一收到數據的第一部分後就開始計算使用的掩碼密鑰,當掩碼後,接著修改尚未發送的作為一個請求出現的幀中的數據(這本質上是和前面段落描述的使用一個已知的或可預測的掩碼密鑰是相同的問題)。
如果需要發送額外的數據或要發送的數據以某種方式修改了,新的修改了的數據必須在一個新的幀中發送,那麼需要一個新的掩碼密鑰。總之,一旦開始傳輸一個幀,對於遠程腳本(應用)來說,內容必須不能是可修改的。
威脅模型用來保護客戶端發送的在一個請求出現的數據。因此,需要掩碼的信道是從客戶端到伺服器的數據。伺服器到客戶端的數據可以作出看起來像一個響應,但為了完成這個請求,客戶端也必須有能力去偽造一個請求。因此,沒必要在兩個方向上掩碼數據(從客戶端到伺服器的數據沒有掩碼)。
儘管掩碼提供了保護,對於客戶端和伺服器沒有掩碼的這種類型的中毒攻擊,非兼容的HTTP代理將依然是脆弱的。
10.4 實現特定限制
實現已經實現-和/或特定平台的有關幀大小或總消息大小的限制,從多個幀重新組裝後,必須保證它們自己不超過這些限制。(例如,一個惡意終端無論是通過單一的大幀(例如,2**60大小)還是通過發送一個長流的分片消息的一部分的小幀,可以設法耗盡它的對等體端點(Peer,即要攻擊的那一方)的記憶體或安裝一個拒絕服務攻擊)。這樣的實現應該對幀大小和從多個幀重組後的總消息大小加以限制。
10.5 WebSocket客戶端驗證
本規範沒有規定任何特定的方式在WebSocket握手期間伺服器可以驗證客戶端。WebSocket伺服器可以使用任何客戶端對普通HTTP伺服器可用的驗證機制,如Cookie,HTTP驗證,或者TLS驗證。
10.6 連接的保密性和完整性
連接的保密性和完整性是通過運行在TLS (wss URIs)上的WebSocket協議提供的。WebSocket實現必須支援TLS並應該在它們的對等端點通訊時使用它。
對於使用TLS的連接,TLS提供的受益量在很大程度上取決於在TLS握手期間協商的演算法強度。例如,一些TLS加密機制不提供連接的保密性。為了實現合理級別的包含,客戶端應該僅適用強TLS演算法。Web安全上下文:用戶介面指南[W3C.REC-wsc-ui-20100812]討論了什麼構成強TLS演算法。[RFC5246]的附錄 A.5 和附錄D.3中提供了額外的指導。
10.7 處理無效數據
傳入的數據必須始終由客戶端和伺服器驗證。如果,在任何時候,一個端點不理解它的數據或違反了一些端點確定的安全輸入標準,或當端點看到一個打開階段握手沒有符合它期望的值(例如,在客戶端請求中不正確的路徑或源),端點可以終止TCP連接。如果在WebSocket握手成功後接收到了無效數據,端點應該在進行_關閉WebSocket連接_之前發送一個帶有適當狀態碼(7.4節)的關閉幀。使用一個帶有適當狀態碼的關閉幀能幫助診斷問題。如果在WebSocket握手期間發送了無效的數據,伺服器應該返回一個適當的HTTP[RFC2616]狀態碼。使用錯誤的編碼發送文本數據是通常出現的一類安全問題。本協議規定一個Text數據類型(而不是Binary或其他類型)的消息包含UTF-8編碼的數據。雖然仍指定了長度,且實現本協議的應用應該使用長度來決定幀從哪真正的結束,但以一個不當的編碼發送數據仍可能打破建立在本協議之上的應用的假設,導致從誤解釋數據到丟失數據或潛在的安全漏洞。
10.8 使用SHA-1的WebSocket握手
本文檔中描述的WebSocket 握手不依賴於任何SHA-1安全特性,例如碰撞性或抗第二前像攻擊(如同[RFC4270]中的描述)。
11 IANA考慮
11.1 註冊新的URI模式
11.1.1 註冊“ws”模式
一個|ws| URI標識一個WebSocket伺服器和資源名稱。
URI模式名稱:ws
狀態: 永久的
URI模式語法:使用ABNF [RFC5234]語法和URI規範[RFC3986]的ABNF終結符:
“ws:” “//” authority path-abempty [ “?” query ]
<path-abempty> 和 <query> [RFC3986]組件形成的資源名發生給伺服器來確定服務期望的類型。其他組件的含義描述在[RFC3986]。
URI模式語義:這個模式的作用僅是使用WebSocket協議打開一個連接。
編碼考慮:上邊定義的語法不包括host組件中的字元,必須按照[RFC3987]從Unicode轉換為ASCII或其替換。為了模式標準化的目的,國際化域名(IDN)形式的host組件和它們轉換的域名程式碼被認為是等價的(參考[RFC3987]5.3.3節)。
上邊定義的語法不包括其他組件中的字元,必須按照定義在URI [RFC3986]和國際化資源標識符(IRI) [RFC3986]規範從Unicode編碼轉換為ASCII,通過首先編碼字元為UTF-8,接著使用它們百分數編碼的形式替換相應的位元組。
應用/協議使用這個URI模式命名:WebSocket協議
互操作性考慮:使用WebSocket需要使用HTTP版本1.1或更高
安全考慮:參考「安全考慮」章節
聯繫方式: HYBI WG [email protected]
作者/變更管理員: IETF [email protected]
參考資源:RFC 6455
11.1.2 註冊“wss”模式
一個|wss| URI標識一個WebSocket伺服器和資源名稱,並標明在受TLS保護的連接之上的通訊(包括標準的TLS的好吃,比如數據保密性和完整性和端點認證)。
URI模式名稱:wss
狀態:永久的
URI模式語法:使用ABNF [RFC5234]語法和URI規範[RFC3986]的ABNF終結符:
“wss:” “//” authority path-abempty [ “?” query ]
<path-abempty> 和 <query>[RFC3986]組件形成的資源名發生給伺服器來確定服務期望的類型。其他組件的含義描述在[RFC3986]。
URI模式語義:這個模式的作用僅是使用WebSocket協議打開一個使用TLS的連接。
編碼考慮:上邊定義的語法不包括host組件中的字元,必須按照[RFC3987]從Unicode轉換為ASCII或其替換。為了模式標準化的目的,國際化域名(IDN)形式的hsot組件和它們轉換的域名程式碼被認為是等價的(參考[RFC3987]5.3.3節)。
上邊定義的語法不包括其他組件中的字元,必須按照定義在URI [RFC3986]和國際化資源標識符(IRI)[RFC3987]規範從Unicode編碼轉換為ASCII,通過首先編碼字元為UTF-8,接著使用它們百分數編碼的形式替換相應的位元組。
應用/協議使用這個URI模式命名:WebSocket協議
互操作性考慮:使用WebSocket需要使用HTTP版本1.1或更高。
安全考慮:參考「安全考慮」章節
聯繫方式: HYBI WG <[email protected]>
作者/變更管理員: IETF <[email protected]>
參考資源: RFC 6455
11.2 註冊「WebSocket」HTTPUpgrade關鍵字
本節按照RFC 2817 [RFC2817]定義了在HTTP Upgrade符號註冊中心註冊一個關鍵字。
符號名稱:WebSocket
作者/變更管理員:IETF <[email protected]>
聯繫方式:HYBI <[email protected]>
參考資源: RFC 6455
11.3 註冊新的HTTP頭欄位
11.3.1 Sec-WebSocket-Key
本節描述了在永久消息頭欄位命名註冊中心[RFC3864]中註冊一個頭欄位。
頭欄位名:Sec-WebSocket-Key
使用協議:http
狀態:標準
作者/變更管理員:IETF
參考資源:RFC 6455
相關資訊:該頭欄位僅用於WebSocket打開階段握手。
|Sec-WebSocket-Key|頭欄位用於WebSocket打開階段握手。它從客戶端發送到伺服器,提供部分資訊用於伺服器檢驗它收到了一個有效的WebSocket握手。這有助於確保伺服器不接收正被濫用來發送數據給毫不知情的WebSocket伺服器的非WebSocket客戶端的連接(例如HTTP客戶端)。
|Sec-WebSocket-Key|頭欄位在一個HTTP請求中不能出現多於一個。
11.3.2 Sec-WebSocket-Extensions
本節描述了在永久消息頭欄位命名註冊中心[RFC3864]中註冊一個頭欄位。
頭欄位名:Sec-WebSocket-Extensions
使用協議:http
狀態:標準
作者/變更管理員:IETF
參考資源:RFC 6455
相關資訊:該頭欄位僅用於WebSocket打開階段握手。
|Sec-WebSocket-Extensions|頭欄位用於WebSocket打開階段握手。它最初是從客戶端發送到伺服器,隨後從伺服器端發送到客戶端,用來達成在整個連接階段的一組協議級擴展。
|Sec-WebSocket-Extensions|頭欄位在HTTP請求中可以出現多次(邏輯上等價於單個|Sec-WebSocket-Extensions|頭欄位包含的所有值)。但是,|Sec-WebSocket-Extensions|頭欄位在一個HTTP響應中必須不出現多於一次。
11.3.3 Sec-WebSocket-Accept
本節描述了在永久消息頭欄位命名註冊中心[RFC3864]中註冊一個頭欄位。
頭欄位名:Sec-WebSocket-Accept
使用協議:http
狀態:標準
作者/變更管理員:IETF
規範文檔: RFC 6455
相關資訊:該頭欄位僅用於WebSocket打開階段握手。
|Sec-WebSocket-Accept|頭欄位用於WebSocket打開階段握手。它從伺服器發送到客戶端來確定伺服器願意啟動WebSocket連接。
|Sec-WebSocket-Accept|頭在一個HTTP響應中必須不出現多於一次。
11.3.4 Sec-WebSocket-Protocol
本節描述了在永久消息頭欄位命名註冊中心[RFC3864]中註冊一個頭欄位。
頭欄位名:Sec-WebSocket-Protocol
使用協議:http
狀態:標準
作者/變更管理員:IETF
規範文檔:RFC 6455
相關資訊:該頭欄位僅用於WebSocket打開階段握手。
|Sec-WebSocket-Protocol|頭欄位用於WebSocket打開階段握手。它從客戶端發送到伺服器端,並從伺服器端發回到客戶端來確定連接的子協議。這使腳本可以選擇一個子協議和確定伺服器同一服務子協議。
|Sec-WebSocket-Protocol|頭欄位在一個HTTP請求中可以出現多次(邏輯上等價於|Sec-WebSocket-Protocol|頭欄位包含的所有值)。但是,|Sec-WebSocket-Protocol|頭欄位在一個HTTP響應必須不出現多於一次。
11.3.5. Sec-WebSocket-Version
本節描述了在永久消息頭欄位命名註冊中心[RFC3864]中註冊一個頭欄位。
頭欄位名:Sec-WebSocket-Version
使用協議:http
狀態:標準
作者/變更管理員:IETF
規範文檔:RFC 6455
相關資訊:該頭欄位僅用於WebSocket打開階段握手。
|Sec-WebSocket-Version|頭欄位用於WebSocket打開階段握手。它從客戶端發送到伺服器端來指定連接的協議版本。這能使伺服器正確解釋打開階段握手和發送數據的隨後數據。如果伺服器不能以安全的方式解釋數據則關閉連接。當從客戶端接收到不匹配伺服器端理解的版本是,WebSocket握手錯誤,|Sec-WebSocket-Version|頭欄位也從伺服器端發送到客戶端。在這種情況下,頭欄位包括伺服器端支援的協議版本。
注意:如果沒有期望更高版本號,必然是向下兼容低版本號。
|Sec-WebSocket-Version|頭欄位在一個HTTP響應中可以出現多次(邏輯行等價於單個|Sec-WebSocket-Version|透過自動包含的所有值)。但是,|Sec-WebSocket-Version|頭欄位在HTTP請求中必須不出現多於一次。
11.4 WebSocket擴展名註冊
本規範根據RFC 5226 [RFC5226]陳述的原則,創建一個新的IANA註冊用於與WebSocket協議一起使用的WebSocket擴展名。
作為本註冊的一部分,IANA維護以下資訊:
擴展標識符:擴展標識符,將被用在註冊到本規範11.3.2節的|Sec-WebSocket-Extensions|頭欄位。其值必須符合定義在本規範9.1節的擴展-符號要求。
擴展通用名稱:拓展名稱,通常稱為擴展。
拓展定義:在擴展用於的WebSocket協議中定義了文檔參考。
已知的不兼容擴展:與此擴展是不兼容的一個擴展標識符列表。
WebSocket擴展名受制於「先來先服務」的IANA註冊策略[RFC5226]。
在此註冊中心沒有初始值。
11.5 WebSocket子協議名註冊
本規範根據RFC 5226 [RFC5226]陳述的原則,創建了一個新的IANA註冊用於與WebSocket協議一曲使用的WebSocket子協議名。
作為本註冊的一部分,IANA維護以下資訊:
子協議標識符:子協議標識符,將被用在註冊到本規範11.3.4節的 |Sec-WebSocket-Protocol|頭欄位。其值必須符合定義在本規範4.1節給出的第10條的符號要求–也就是,其值必須是RFC 2616 [RFC2616]定義的一個符號。
子協議通用名稱:子協議名稱,通常稱為子協議
子協議定義:在子協議用於定義了WebSocket協議的文檔參考
WebSocket子協議名受制於「先來先服務」的IANA註冊策略[RFC5226]。
11.6 WebSocket Version Number Registry WebSocket版本號註冊
本規範依據RFC 5226 [RFC5226]陳述的原則,創建了一個新的IANA註冊用於與WebSocket協議一起使用的WebSocket版本號。
作為註冊的一部分,IANA維護以下資訊:
版本號: 用於|Sec-WebSocket-Version|的版本號指定在本規範4.1節。其值必須是一個在0到255(包括)之間的非負整數。
參考:RFC請求一個新的版本號或帶版本號的草案名稱(見下文)。
狀態:「臨時的」或「標準的」。參考下面的說明。
一個版本號被臨時指定為「臨時的」或「標準的」。
「標準的」版本號是記錄在一個RFC中並用來識別一個主要的、穩定的WebSocket協議版本,例如本RFC定義的版本。「標準的」版本號受制於「IETF評審「IANA註冊策略。[RFC5226].
“Interim”的版本號記錄在一個Internet草案中用並用於幫助實現這識別和與部署的WebSocket版本互操作,例如在公布這個RFC之前指定的版本。「臨時的」版本號受制於「專家評審」IANA註冊策略[RFC5226],HYBI工作組主席(或,如果工作組關閉了,IETF應用區域的區域董事)將是初始的指定專家。
IANA已經添加如下的初始值到註冊中心:
+--------+-----------------------------------------+----------+ |Version | Reference | Status | | Number | | | +--------+-----------------------------------------+----------+ | 0 + draft-ietf-hybi-thewebsocketprotocol-00 | Interim | +--------+-----------------------------------------+----------+ | 1 + draft-ietf-hybi-thewebsocketprotocol-01 | Interim | +--------+-----------------------------------------+----------+ | 2 + draft-ietf-hybi-thewebsocketprotocol-02 | Interim | +--------+-----------------------------------------+----------+ | 3 + draft-ietf-hybi-thewebsocketprotocol-03 | Interim | +--------+-----------------------------------------+----------+ | 4 + draft-ietf-hybi-thewebsocketprotocol-04 | Interim | +--------+-----------------------------------------+----------+ | 5 + draft-ietf-hybi-thewebsocketprotocol-05 | Interim | +--------+-----------------------------------------+----------+ | 6 + draft-ietf-hybi-thewebsocketprotocol-06 | Interim | +--------+-----------------------------------------+----------+ | 7 + draft-ietf-hybi-thewebsocketprotocol-07 | Interim | +--------+-----------------------------------------+----------+ | 8 + draft-ietf-hybi-thewebsocketprotocol-08 | Interim | +--------+-----------------------------------------+----------+ | 9 + Reserved | | +--------+-----------------------------------------+----------+ | 10 + Reserved | | +--------+-----------------------------------------+----------+ | 11 + Reserved | | +--------+-----------------------------------------+----------+ | 12 + Reserved | | +--------+-----------------------------------------+----------+ | 13 + RFC 6455 | Standard | +--------+-----------------------------------------+----------+
11.7. WebSocket關閉程式碼註冊
本規範依據RFC 5226 [RFC5226]陳述的原則,創建了一個新的IANA註冊用於WebSocket關閉程式碼。
作為本註冊的一部分,IANA維護以下資訊:
狀態碼:狀態碼錶示一個按照本文檔7.4節的WebSocket連接關閉的原因。狀態時一個在1000到4999(包括)之間的一個整數數字。
含義:狀態碼的含義。每一個狀態碼都必須有唯一的含義。
聯繫方式:保留狀態程式碼實體的聯繫方式。
參考:穩定的文檔要求狀態碼並定義它們的含義。在1000-2999範圍內的狀態碼是必須的且推薦的狀態碼在3000-3999範圍內。
WebSocket關閉程式碼根據它們的範圍受不同的註冊要求。本協議請求使用的狀態碼和其後續版本或拓展受制於「標準功能」、「規定要求」(這意味著「指定專家」)或「IESG審查」IANA註冊策略中的任何一個,且應該允許在1000-2999範圍內。庫、框架和應用請求使用的狀態碼受制於「先來先服務」IANA註冊策略且應該允許在3000-3999範圍內。4000-4999範圍的狀態碼被指定用於私有使用。請求應該指出他們要求的狀態碼是用於WebSocket協議(或未來版本的協議)、擴展,或庫/框架/應用。
IANA已經添加如下初始值到註冊中心:
|Status Code | Meaning | Contact | Reference | -+------------+-----------------+---------------+-----------| | 1000 | Normal Closure | [email protected] | RFC 6455 | -+------------+-----------------+---------------+-----------| | 1001 | Going Away | [email protected] | RFC 6455 | -+------------+-----------------+---------------+-----------| | 1002 | Protocol error | [email protected] | RFC 6455 | -+------------+-----------------+---------------+-----------| | 1003 | Unsupported Data| [email protected] | RFC 6455 | -+------------+-----------------+---------------+-----------| | 1004 | ---Reserved---- | [email protected] | RFC 6455 | -+------------+-----------------+---------------+-----------| | 1005 | No Status Rcvd | [email protected] | RFC 6455 | -+------------+-----------------+---------------+-----------| | 1006 | Abnormal Closure| [email protected] | RFC 6455 | -+------------+-----------------+---------------+-----------| | 1007 | Invalid frame | [email protected] | RFC 6455 | | | payload data | | | -+------------+-----------------+---------------+-----------| | 1008 | Policy Violation| [email protected] | RFC 6455 | -+------------+-----------------+---------------+-----------| | 1009 | Message Too Big | [email protected] | RFC 6455 | -+------------+-----------------+---------------+-----------| | 1010 | Mandatory Ext. | [email protected] | RFC 6455 | -+------------+-----------------+---------------+-----------| | 1011 | Internal Server | [email protected] | RFC 6455 | | | Error | | | -+------------+-----------------+---------------+-----------| | 1015 | TLS handshake | [email protected] | RFC 6455 | -+------------+-----------------+---------------+-----------|
11.8 WebSocket操作碼註冊
本規範根據RFC 5226 [RFC5226]陳述的原則,創建一個新的IANA註冊用於WebSocket操作碼。
作為本註冊的一部分,IANA維護以下資訊:
操作碼:操作碼錶示WebSocket幀的幀類型,定義在5.2節。操作碼是一個0到15(包括)之間的數字。
含義:狀態碼值的含義。
參考:規範要求的操作碼。
WebSocket狀態碼受制於「標準功能」IANA註冊策略[RFC5226]。
IANA已經添加了如下的初始值到註冊中心:
|Opcode | Meaning | Reference | -+--------+-------------------------------------+-----------| | 0 | Continuation Frame | RFC 6455 | -+--------+-------------------------------------+-----------| | 1 | Text Frame | RFC 6455 | -+--------+-------------------------------------+-----------| | 2 | Binary Frame | RFC 6455 | -+--------+-------------------------------------+-----------| | 8 | Connection Close Frame | RFC 6455 | -+--------+-------------------------------------+-----------| | 9 | Ping Frame | RFC 6455 | -+--------+-------------------------------------+-----------| | 10 | Pong Frame | RFC 6455 | -+--------+-------------------------------------+-----------|
11.9 WebSocket幀頭位註冊
本規範依據RFC 5226 [RFC5226] 陳述的原則,創建了一個新的IANA註冊用於WebSocket 幀頭位。此註冊控制的位分別標記為5.2節的RSV1,RSV2和 RSV3。
這些位被保留用於未來版本或本規範的擴展。
WebSocket幀頭位分配受制於「標準功能」IANA註冊策略[RFC5226]。
12 其他規範使用WebSocket協議
WebSocket協議的目的是被另一個規範使用來提供一個通用機制來動態作者-定義內容,例如,在一個規範中定義一個腳本API。
這樣的規範首先需要_建議一個WebSocket連接_,該演算法是:
1、目的地,包含一個/host/和一個 /port/。
2、一個 /resource name/,運行在一個host和port標識多個服務。
3、一個 /secure/標記,如果連接時加密的則為true,否則為false。
4、一個源[RFC6454]的ASCII序列化,負責連接。
5、可選的,一個字元串標識一個協議,層疊在WebSocket連接之上。
/host/、 /port/、 /resource name/和 /secure/標記通常從一個URI中使用該步驟解析一個WebSocket URI組件獲得。如果沒有指定一個WebSocket,則這些步驟失敗。
如果在任何時候連接將被關閉,那麼規範需要使用_關閉WebSocket 連接_演算法(7.1.1節)。
7.1.4節定義了什麼時候_ WebSocket 連接關閉_。
當打開一個連接,規範將需要處理什麼時候_已經接收了一個WebSocket 消息_的情況(6.2節)。
要發送一些數據/data/到一個打開的連接,規範需要_發送一個WebSocket 消息_(6.1節)
13 致謝
略
14參考資料
略
The WebSocket Protocol [RFC6455],張開濤[譯],部分內容有修改。