[翻譯]WebSocket協議第一章——Introduction

概述

本文爲WebSocket協議的第一章,本文翻譯的主要內容爲針對整個WebSocket進行一個簡單而又全面的介紹。通過這篇文章我們能夠對WebSocket有一個整體的大致瞭解。

1 介紹

本章爲協議正文內容的第一章(Introduction)。

1.1 背景

此章節爲非規範章節。

在歷史上,創建一個客戶端和服務端的雙向數據Web應用(例如IM應用和遊戲應用)需要向服務端頻繁發送不同於一般HTTP請求的HTTP輪詢請求來從服務端上游更新數據。

這個方法有許多的問題:

  • 服務端被迫使用大量的的潛在的TCP連接與客戶端進行交互:一部分是用來發送數據,而另一部分是用來接收數據。
  • 應用層無線傳輸協議(HTTP)開銷較大,每一個客戶端到服務端的消息都有一個HTTP頭。
  • 客戶端腳本必須包含一個發送和接收對應的映射表來進行對應數據處理。

一個簡單的解決方案是使用一個簡單的TCP鏈接來進行雙向數據傳輸。這就是WebSocket提供的能力。結合WebSocket的API,它能夠提供一個可以替代HTTP輪詢的方法來滿足Web頁面和遠端服務器的雙向數據通信。

相同的技術可以被用到許多的Web應用:遊戲、股票應用、多人協作應用、與後端服務實時交互的用戶接口等。

WebSocket協議設計的原因是取代已經存在的使用HTTP作爲傳輸層的雙向通信技術,從而使得已經存在的基礎服務(如代理、過濾器、認證服務)能夠受益。這種技術是基於效率和可靠性權衡後來進行實現的,而HTTP協議最初也不是用來做雙向數據通信的。WebSocket協議嘗試實現基於現有的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: 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

客戶端請求的第一行(leading line)遵循了HTTP請求行的格式。

服務端的第一行(leading line)遵循了HTTP狀態行的格式。

HTTP請求行和狀態行的規範定義在RFC2616

在兩個協議中,第一行header下面是一組無序的header字段。這些header字段包含的內容在本文的第四節。另外的header字段如cookies,也有可能存在。格式和解析頭信息被定義在了RFC2616

當客戶端和服務端都發送了他們的握手協議,並且當握手已經成功,那麼數據傳輸就開始了。這是一個雙方都可以獨立發送任意數據的雙向通信渠道。

在握手成功以後,客戶端和服務端傳輸的數據來回傳輸的數據單位,我們在規範中稱爲消息(messages)。在傳輸中,一條消息有一個或者多個幀組成。WebSocket中的消息不需要對應特定網絡層中的幀,一條零散的消息可能由中間人合併或者拆分成網絡層的幀。

幀有關聯的類型。同一條消息的每一幀都包含相同類型的數據。通常來說,它可以是文本數據(UTF-8編碼)、二進制數據(留給應用解析的數據)和控制幀數據(不是用來傳輸數據,而是用來作爲協議層的特定符號,如關閉連接幀)。當前版本的協議定義了6中控制幀類型並且預留了10個保留類型。

1.3 連接握手

此節爲非規範章節。

在連接握手爲了與基於HTTP的服務端軟件和中介兼容,因此一個獨立的端口既能夠同時滿足HTTP客戶端來與服務進行交互,又能夠滿足WebSocket客戶端與服務進行交互。最終,WebSocket客戶端的握手是一個基於HTTP的升級請求:

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

遵照RFC2616,客戶端在握手過程中發送的header字段可能是亂序的,所以收到的header字段的順序不同也沒有太大影響。

GET方法的請求URI(Request-URI)是用於定義WebSocket連接的終端,允許同一個IP對多個域名提供服務,也允許多個WebSocket終端連接同一個服務器。

客戶端在每一個握手的Hostheader裏面包含了一個主機域名。所以客戶端和服務端都可以校驗哪些域名在使用中。

另外的header字段是用來確定WebSocket協議的選項。這個版本中提供的特定選項是子協議選擇(Sec-WebSocket-Protocol)、客戶端支持的擴展列表(Sec-WebSocket-Extensions)、Originheader字段等。請求header字段Sec-WebSocket-Protocol可以用來標識哪些子協議(基於WebSocket的應用高層協議)是客戶端可以支持的。服務端回從中選擇零或者一個支持的協議並且在響應握手中輸出它選擇的那個協議。

Sec-WebSocket-Protocol: chat

服務端也可以設置cookie相關字段來設置cookie相關屬性,具體文檔見RFC6265

1.4 結束握手

此節爲非規範章節。

結束握手遠比連接握手簡單。

任何一端都可以發送一個包含特定關閉握手的控制幀數據(詳情見5.5.1節)。收到此幀後,另一端在不發送任何數據後會發送一個結束幀作爲響應。收到另一端的結束幀後,最開始發送控制幀的端在沒有數據需要發送時,就會安全的關閉此連接。

在發送了一個表明連接需要被關閉的控制幀後,這個客戶端不會再發送任何的數據;在收到一個表明連接需要被關閉的控制幀後,這個客戶端會丟棄此後的所有數據。

這樣比兩邊同時發起握手要更加安全。

這個結束握手的目標是來補充TCP結束握手中的一些內容(FIN/ACK),而這是因爲TCP結束握手在端與端之間並不一定可靠,尤其是有代理和其他的網絡中介時會變得不可靠。

在發送關閉幀等待接受另一端的響應關閉幀時,在某些情況下可以避免數據的不必要丟失。例如,在某些平臺中,如果一個socket在接收隊列有數據時被關閉,會發送一個RST包,儘管數據還在等待被讀取,這也會導致接收到RST的一方數據接收失敗。

1.5 設計哲學

此節爲非規範章節。

WebSocket協議設計的原理就是設計爲最的小框架(唯一的約束就是使這個協議是基於幀而不是流,並且可以支持Unicode文本和二進制幀兩者中的任意一種)。在基於WebSocket的應用層中,元數據是應該分層的,就像基於TCP的應用層(例如HTTP)一樣。

從概念上來看,WebSocket層是基於TCP實現的,增加了以下的內容:

  • 增加了一個基於瀏覽器的同源策略模型
  • 增加了一個地址和協議命名機制來在同一個端口上支持多個服務,在同一個IP地址自持多個主機名
  • 在TCP協議上分層構建框架機制回到TCP使用的IP包機制,但是沒有長度限制
  • 包含一個設計用於處理有代理和其他網絡中介的情況的額外的結束握手協議

除此之外,WebSocket沒有增加任何東西。基本上WebSocket的的目標是在約束的條件下像腳本提供儘可能接近原生的TCP的Web服務。它同時考慮了服務器在進行握手和處理有效的HTTP升級請求時,可以和HTTP共用一個服務。大家也可以使用其他協議來建立從客戶端到服務端的消息通信,但WebSocket的協議的目的是爲了提供一個相對簡單的可以和HTTP共存,並且依賴於HTTP基礎設施(如代理)的協議。這個非常接近TCP的協議因爲基於安全的基礎設施和針對性的能夠簡單使用和讓事情變得更簡單的補充(例如消息語義的補充),因此可以安全使用。

這個協議具有可擴展性,未來的版本可能會引入一些新的概念如多路複用。

1.6 安全模型

此節爲非規範章節。

當WebSocket協議在web網頁中應用時,WebSocket協議在Web頁面與WebSocket服務器建立連接時使用基於web瀏覽器的同源策略模型。所以說,當WebSocket協議在一個特定的客戶端(不是web瀏覽器裏面的網頁)直接使用時,同源策略模型就不生效了,客戶端可以接受任意的源數據。

該協議無法與已經存在的如SMTP(RFC5421)和HTTP協議的服務器建立連接,如果需要的話,HTTP服務器可以選擇支持該協議。該協議還實現了嚴格約束的握手過程和限制數據不能在握手完成和建立連接之前插入數據進行傳輸(因此限制了許多被影響的服務器)。

WebSocket服務器同樣無法與其他協議尤其是HTTP建立連接。例如,一個HTML“表單”可能會提交給一個WebSocket服務器。WebSocket服務端只能讀取包含特定的由WebSocket客戶端發送的字段的握手數據。尤其是在編寫這個規範時,攻擊者不能只使用HTML和JavaScript APIs的Web瀏覽器來發送以Sec-開頭的字段。

1.7 與TCP和HTTP的關係

此節爲非規範章節。

WebSocket協議是獨立的基於TCP的協議。他和HTTP的唯一關係是建立連接的握手操作的升級請求是基於HTTP服務器的。

WebSocket默認使用80端口進行連接,而基於TLS(RFC2818)的WebSocket連接是基於443端口的。

1.8 建立連接

此節爲非規範章節。

當建立了一個和HTTP服務器共享端口的連接時(這種情況很有可能發送在與80和443端口通信上),這個鏈接將會給HTTP服務器發送一個常規的GET請求來進行升級。在一個IP地址和一個單一的服務器來應對單一主機名的通信這種相對簡單的設置上,基於WebSocket協議的系統可以通過一個更加實用的方法來進行部署。在更詳細的設置(例如負載均衡和多服務器),與HTTP服務器分開的專屬的WebSocket連接集羣可能更加易於管理。在編寫這個規範時,我們應該知道在80端口和443端口建立WebSocket連接的成功率是不同的,在443端口上面建立的連接很明顯更容易成功,儘管這可能隨着時間的變化而改變。

1.9 使用WebSocket協議的子協議

客戶端可以通過在握手階段中的Sec-WebSocket-protocol字段來請求服務端使用指定的子協議。如果指定了這個字段,服務器需要包含相同的字段,並且從子協議的之中選擇一個值作爲建立連接的響應。

子協議的名稱可以按照第11.5節的方法進行註冊。爲了避免潛在的衝突,推薦使用包含ASCII碼的域名名稱作爲子協議名。例如,Example Corporation創造了在Web上通過多個服務器實現的一個聊天子協議(Chat subprotocol),他們可以叫做chat.example.com。如果Example Organization創造了他們相對的子協議叫做chat.example.org,這兩個子協議可以被服務器同時實現,服務器可以根據客戶端來動態的選擇使用哪一個子協議。

子協議也可以通過修改名字的方式來向後兼容,例如:將bookings.example.net改爲v2.bookings.example.net。WebSocket客戶端能夠完全的區分這些子協議。向後兼容的版本控制可以通過複用相同的子協議字符和小心設計的子協議實現來保證這種擴展性。

總結

本文通過對WebSocket進行了一個全面的大致介紹,能夠讓大家對於WebSocket相關協議內容有一個初步的理解。

後續會持續對WebSocket協議進行翻譯,有興趣瞭解的同學可以持續關注下。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章