一文讀懂WebSocket

  • 2019 年 10 月 7 日
  • 筆記

什麼是WebSocket

WebSocket是一種網路協議,在OSI模型中,WebSocket協議與HTTP協議一樣,都屬於最頂層的應用層協議。有些朋友可能會有疑問,既然已經有了HTTP協議,為什麼還需要WebSocket協議呢?WebSocket協議相對於HTTP協議到底有什麼優勢呢?我們考慮以下場景,假設我們有一個網頁版的類似於QQ一樣的聊天網站,瀏覽器需要實時地從伺服器獲取最新的聊天數據,如果使用HTTP協議的話,通常只能通過瀏覽器不斷地輪詢伺服器來獲取最新的聊天數據,因為HTTP協議不支援服務端推送(雖然HTTP2已經支援服務端推送,但是HTTP2的服務端推送跟我們今天講的服務端推送還是有區別的,後續有時間再進行介紹)。通過客戶端不斷輪詢的缺點是會造成流量浪費和性能損耗。而使用WebSocket協議則不需要客戶端輪詢就能獲取伺服器最新的數據,因為WebSocket協議支援服務端推送,在上述聊天應用中,當服務端有新消息到來時,只需要通過WebSocket協議推送給客戶端就行了,這樣一來既能保證服務端消息的實時性,也能減少性能損耗。

WebSocket協議概述

WebSocket是一種在單個TCP連接上進行全雙工通訊的協議,其使得客戶端和伺服器之間的數據交換變得更加簡單,允許服務端主動向客戶端推送數據。在WebSocket API中,瀏覽器和伺服器只需要完成一次握手,兩者之間就直接可以創建持久性的連接,並進行雙向數據傳輸。WebSocket使用和 HTTP 相同的 TCP 埠,可以繞過大多數防火牆的限制。默認情況下,WebSocket協議使用80埠;運行在TLS之上時,默認使用443埠。

WebSocket協議建立連接的時候需要握手,握手過程中需要藉助HTTP協議來完成,當連接建立後,就可以使用WebSocket協議進行通訊,通訊結束後,通訊雙方都可以關閉連接。其中,WebSocket協議握手階段是WebSocket協議的基礎,接下來將重點講述WebSocket協議如何通過HTTP協議進行握手,從而建建立連接。

WebSocket握手

WebSocket協議通過HTTP協議進行握手是為了兼容基於HTTP的伺服器端軟體和中間設施,使同一個埠能夠接受HTTP客戶端和WebSocket客戶端,為了這個目的,WebSocket客戶端的握手是HTTP請求的升級。客戶端和服務端需要通過一次HTTP請求與響應來進行協議升級,具體步驟如下:

1.客戶端向服務端發送協議升級請求

客戶端發送的HTTP報文示例如下:

GET / HTTP/1.1  Upgrade: websocket  Connection: Upgrade  Host: example.com  Origin: http://example.com  Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ==  Sec-WebSocket-Version: 13

可以看到,其必須是一個GET請求,且HTTP版本必須為1.1。除此之外,其還有如下要求:

  • 必須帶有Host請求頭,其值為要請求的主機名。
  • 必須帶有Upgrade請求頭,且其值必須為websocket,表示這個HTTP請求的目的是要申請升級到websocket協議,而不是其他協議。
  • 必須帶有Connection請求頭,其值必須為Upgrade,表示這個HTTP請求是一個協議升級請求。
  • 必須帶有Sec-WebSocket-Key請求頭,且其值為以BASE-64編碼的隨機字元串。伺服器端會用這些數據來構造出一個SHA-1的資訊摘要。把「Sec-WebSocket-Key」的值加上一個特殊字元串「258EAFA5-E914-47DA-95CA-C5AB0DC85B11」,然後計算SHA-1摘要,之後進行BASE-64編碼,將結果做為「Sec-WebSocket-Accept」響應頭的值,返回給客戶端。如此操作,可以盡量避免普通HTTP報文被誤認為WebSocket協議握手報文。
  • 如果這個請求是從瀏覽器發出的,那麼還必須帶有Origin請求頭。
  • 必須帶有Sec-WebSocket-Version請求頭,且其值必須為13,表示使用的WebSocket版本為13。

2.服務端響應客戶端的協議升級請求

當服務端接收到客戶端的協議升級請求時,服務端會判斷是否要接受該請求,並返回相應的HTTP響應報文給客戶端。如果客戶端發送的HTTP請求滿足上述所有要求,那麼服務端將會接受該協議升級請求,並返回如下響應報文:

HTTP/1.1 101 Switching Protocols  Upgrade: websocket  Connection: Upgrade  Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s=

需要注意的是,該響應報文的狀態碼並不是常見的200狀態碼,而是101,那麼101狀態碼錶示什麼意思呢?其表示的是服務端同意客戶端的切換協議請求,從該報文的狀態碼描述Switching Protocols可得知。具體來說,就是同意將當前的HTTP協議切換到WebSocket協議。

其次,該響應報文還必須滿足如下要求:

  • 必須包含Upgrade響應頭,並且其值必須為websocket。
  • 必須包含Connection響應頭,且其值必須為Upgrade。
  • 必須包含Sec-WebSocket-Accept響應頭,其值根據客戶端的Sec-WebSocket-Key請求頭的值計算而來,具體計算規則上文已經介紹。客戶端就是根據這個Sec-WebSocket-Accept的值來判斷該響應報文是否來自真正的WebSocket服務端,如果該值與客戶端計算出來的值不相同,那麼客戶端將會拒絕建立WebSocket連接,同時該響應頭還能避免將普通的HTTP報文當成WebSocket協議握手報文。

3.客戶端檢查服務端的響應報文

當客戶端接收到服務端的響應報文後,會檢查其HTTP狀態碼是不是101,以及檢查是否有Upgrade、Connection與Sec-WebSocket-Accept響應頭,並且他們的值是否與預期的相同,只有上述條件都滿足後,WebSocket協議握手階段才算完成,否則客戶端將拒絕建立WebSocket協議連接。

當連接建立後,客戶端與服務端就可以通過WebSocket協議進行雙向通訊了。

總結

以上就是WebSocket協議的簡要介紹以及握手的全過程了,關於WebSocket協議的更多細節可以閱讀rfc6455(https://tools.ietf.org/html/rfc6455#page-41)進行深入學習。