【譯】 WebSocket 協議第七章——關閉鏈接(Closing the Connection)

概述

本文爲 WebSocket 協議的第七章,本文翻譯的主要內容爲 WebSocket 鏈接關閉相關內容。算法

有興趣瞭解該文檔以前幾章內容的同窗能夠見:瀏覽器

關閉鏈接(協議正文)

7.1 定義

7.1.1 關閉 WebSocket 鏈接

關閉 WebSocket 鏈接,終端須要關閉底層的 TCP 鏈接。終端須要使用一個方法來乾淨的關閉TCP鏈接,還有 TLS 會話,若是可能的話,拋棄後面可能受到的任意字符。終端可能會在須要的時候,經過任何方式來關閉鏈接,例如在收到攻擊時。服務器

在底層的 TCP 鏈接中,一般大多數狀況下,服務端應該先關閉,因此是服務端而不是客戶端保持 TIME_WAIT 狀態(由於客戶端先關閉的話,這會阻止服務端在2 MSL 內從新打開這條鏈接,而若是服務器處於 TIME_WAIT 狀態下,若是收到了一個帶有更大序列號的新的 SYN 包時,也可以當即響應從新打開鏈接,從而不會對服務器產生影響)。反常狀況(例如在合理的時間後,服務端收到一個 TCP 關閉包)下,客戶端應該開始關閉 TCP 鏈接。像這樣的,當服務端進入關閉 WebSocket 鏈接狀態時,它應該馬上準備關閉 TCP 鏈接,而後當客戶端客戶端準備關閉鏈接時,他應該等待服務端的 TCP 關閉包。閉包

用 C 語言的 Berkeley socket 做爲例子來展現如何完全的關閉鏈接,一端須要用 SHUP_WR 調用 shutdown() 方法,調用 recv() 直到得到一個值爲 0 的表示對面也準備有序關閉鏈接的返回值,而後最後調用 close() 來關閉 socket 通道。框架

7.1.2 開始進行 WebSocket 關閉握手

用一個狀態碼 code (第 7.4 節)和一個可選的關閉緣由 reason (第 7.1.6 節)來開始 WebSocket 關閉握手,終端必須發送一個在第 5.5.1 節中描述的同樣的關閉幀,將狀態碼設置爲 code 字段,將關閉緣由設置爲 reaons 字段。一旦終端已經發送和收到了關閉控制幀,那麼終端應該像第 7.1.1 節中定義的同樣關閉 WebSocket 鏈接socket

7.1.3 已經開始 WebSocket 關閉握手

在發送或者收到了關閉幀時,咱們能夠說已經開始 WebSocket 關閉握手,而且 WebSocket 鏈接的狀態已經到了「關閉中」(CLOSING)狀態。post

7.1.4 WebSocket 鏈接已關閉

當底層的 TCP 鏈接關閉後,咱們能夠說WebSocket 鏈接已關閉,而且 WebSocket 鏈接已經到了」關閉「(CLOSED)狀態。若是 TCP 鏈接在 WebSocket 關閉握手完成以後已經關閉,那麼咱們能夠說 WebSocket 鏈接已經被完全關閉。ui

若是 WebSocket 鏈接沒有被創建,咱們也說WebSocket已經關閉,可是不完全編碼

7.1.5 WebSocket 關閉狀態碼

就像在第 5.5.1 和第 7.4 節中定義的同樣,關閉幀能夠包含一個關閉的狀態碼和指定的緣由。WebSocket 鏈接的關閉多是同時由另外一個終端發起。WebSocket 關閉狀態碼是在第 7.4 節中定義的在第一關閉幀中的由實現該協議的應用程序接收的狀態碼。若是關閉幀中沒有包含狀態碼,WebSocket 關閉狀態碼被默認爲1005。若是WebSocket 已經關閉而且終端沒有收到任何的關閉幀(例如發生了可能底層的傳輸鏈接忽然丟失的狀況),那麼WebSocket 關閉狀態碼被默認爲1006。翻譯

注:兩個終端可能沒有就WebSocket 關閉狀態碼的值達成一致。例如:若是遠端發送一個關閉幀,可是本地應用沒有從它的 socket 緩衝區中讀到關閉幀的數據,同時本地應用單獨的決定關閉鏈接而且發送了一個關閉幀,那麼兩個終端都發送了而且會收到一個關閉幀,同時不會發送更多的關閉幀。每個終端會看到另外一個終端發送過來的WebSocket 關閉狀態碼的狀態碼。像這樣的,在這個示例裏面,有可能兩個終端都沒有協商過WebSocket 關閉狀態碼,兩個終端都幾乎在同一時間單獨開始 WebSocket 關閉握手

7.1.6 WebSocket 鏈接關閉緣由

像第 5.5.1 節和第 7.4 節中定義的同樣,一個關閉幀可能包含一個用於關閉的表示緣由的狀態碼,而後是 UTF-8 編碼的數據,數據的解析方式是留給終端來解釋,而不在這個協議中定義。一個正在關閉中的 WebSocket 鏈接多是同時從另外一端開始的。WebSocket 鏈接關閉緣由是實現了該協議的應用收到的緊跟在狀態碼(第 7.4 節)以後的包含在第一個關閉控制幀中的 UTF-8 編碼數據。若是在關閉控制幀中沒有這些數據,那麼WebSocket 鏈接關閉緣由的值就是一個空字符串。

注:和在第 7.1.5 中被提到的邏輯同樣,兩個終端可能沒有協商過WebSocket 鏈接關閉緣由

7.1.7 WebSocket 鏈接失效

某些算法和規範要求終端有WebSocket 鏈接失效。爲了實現這些,客戶端必須關閉 WebSocket 鏈接,而且能夠用一個合適的方式向用戶上報相關問題(尤爲是對開發者有幫助的內容)。類似的,爲了實現這個,服務端必須關閉 WebSocket 鏈接,而且應該用日誌記錄這個問題。

若是在此以前WebSocket 已經創建鏈接,此時終端須要讓WebSocket 鏈接失效,那麼在進行關閉 WebSocket 鏈接以前,終端須要發送一個包含恰當的狀態碼(第 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.4 狀態碼

當關閉一個鏈接時(如:在開始握手已經完成後,發送一個關閉幀),終端可能會說明關閉的緣由。終端的這個緣由的描述和終端應該採起的行動,在這個文檔中都沒有說明。這個文檔提早定義了一些可能用於擴展、框架和終端應用的狀態碼和狀態碼範圍。這些狀態碼和任何有關聯的的文本消息在關閉幀中都是可選的。

7.4.1 定義狀態碼

在發送一個關閉幀時,終端能夠提早定義以下的狀態碼。

1000

1000 表示一個正常的關閉,意味着鏈接創建的目標已經完成了。

1001

1001 表示終端已經「走開」,例如服務器停機了或者在瀏覽器中離開了這個頁面。

1002

1002 表示終端因爲協議錯誤停止了鏈接。

1003

1003 表示終端因爲收到了一個不支持的數據類型的數據(如終端只能怪理解文本數據,可是收到了一個二進制數據)從而關閉鏈接。

1004

保留字段。這意味着這個狀態碼可能會在未來被定義。

1005

1005 是一個保留值而且不能被終端當作一個關閉幀的狀態碼。這個狀態碼是爲了給上層應用表示當前沒有狀態碼。

1006

1006 是一個保留值而且不能被終端當作一個關閉幀的狀態碼。這個狀態碼是爲了給上層應用表示鏈接被異常關閉如沒有發送或者接受一個關閉幀這種場景的使用而設計的。

1007

1007 表示終端由於收到了類型不連續的消息(如非 UTF-8 編碼的文本消息)致使的鏈接關閉。

1008

1008 表示終端是由於收到了一個違反政策的消息致使的鏈接關閉。這是一個通用的狀態碼,能夠在沒有什麼合適的狀態碼(如 1003 或者 1009)時或者可能須要隱藏關於政策的具體信息時返回。

1009

1009 表示終端因爲收到了一個太大的消息沒法進行處理從而關閉鏈接。

1010

1010 表示終端(客戶端)由於預期與服務端協商一個或者多個擴展,可是服務端在 WebSocket 握手中沒有響應這個致使的關閉。須要的擴展清單應該出如今關閉幀的緣由(reason)字段中。

1001

1001 表示服務端由於遇到了一個意外的條件阻止它完成這個請求從而致使鏈接關閉。

1015

1015 是一個保留值,不能被終端設置到關閉幀的狀態碼中。這個狀態碼是用於上層應用來表示鏈接失敗是由於 TLS 握手失敗(如服務端證書沒有被驗證過)致使的關閉的。

7.4.2 保留狀態碼範圍

0-999

0-999 的狀態碼都沒有被使用。

1000-2999

1000-2999 的狀態碼是在這個文檔、未來的修訂和擴展中定義的保留字段,用於永久的可用的公共文檔。

3000-3999

3000-3999 的狀態碼是保留給庫、框架和應用使用的。這些狀態碼被IANA直接註冊了。這些狀態碼在這篇文檔中沒有進行解釋。

4000-4999

4000-4999 的狀態碼是保留下來私用的,所以這些狀態碼不能被註冊。這些狀態碼可使用在 WebSocket 應用以前的協議上。這些狀態碼在這篇文檔中沒有進行解釋。

相關文章
相關標籤/搜索