為關(guān)閉WebSocket連接,端點需關(guān)閉底層TCP連接。端點應(yīng)該使用一個方法完全地關(guān)閉TCP連接,以及TLS會話,如果合適,丟棄任何可能已經(jīng)接收的尾隨的字節(jié)。當必要時端點可以通過任何可用的手段關(guān)閉連接,例如當受到攻擊時。
底層TCP連接,在大多數(shù)正常情況下,應(yīng)該首先被服務(wù)器關(guān)閉,所以它持有TIME_WAIT狀態(tài)而不是客戶端(因為這會防止它在2個報文最大生存時間(2MLS)內(nèi)重新打開連接,然而當一個新的帶有更高的seq number的SYN時沒有對應(yīng)的服務(wù)器影響TIME_WAIT連接被立即重新打開)。在異常情況下(例如在一個合理的時間量后沒有接收到服務(wù)器的TCP Close)客戶端可以發(fā)起TCP Close。因此,當服務(wù)器被指示關(guān)閉WebSocket連接,它應(yīng)該立即發(fā)起一個TCP Close,且當客戶端被知識也這么做時,它應(yīng)該等待服務(wù)器的一個TCP Close。
例如一個如何使用Berkeley socket在C中得到完全地關(guān)閉的例子,一端會在socket上以SHUT_WR調(diào)用shutdown(),調(diào)用recv()直到獲得一個指示那個節(jié)點也已經(jīng)執(zhí)行了一個有序關(guān)閉的0返回值,且最終在socket上調(diào)用close()方法。
為了啟動WebSocket關(guān)閉階段握手,其帶有一個狀態(tài)碼(7.4節(jié))/code/和一個可選的關(guān)閉原因(7.1.6節(jié))/reason/,一個端點必須按照5.5.1節(jié)的描述發(fā)送一個Close控制幀,其狀態(tài)碼設(shè)置為/code/且其關(guān)閉原因設(shè)置為/reason/。一旦一個端點已經(jīng)發(fā)送并接收到一個Close控制幀,那個端點應(yīng)該按照7.1.1節(jié)的描述關(guān)閉WebSocket連接。
一旦發(fā)送或接收到一個Close控制幀,這就是說,WebSocket 關(guān)閉階段握手已啟動,且WebSocket連接處于CLOSING狀態(tài)。
當?shù)讓覶CP連接已關(guān)閉,這就是說WebSocket連接已關(guān)閉且WebSocket連接處于CLOSED狀態(tài)。如果TCP連接在WebSocket關(guān)閉階段我是已經(jīng)完成后被關(guān)閉,WebSocket連接被說成已經(jīng)完全地關(guān)閉了。 如果WebSocket連接不能被建立,這就是說,WebSocket連接關(guān)閉了,但不是完全的。
按照5.5.1和7.4節(jié)的定義,一個Close控制幀可以包含一個表示關(guān)閉原因的狀態(tài)碼。一個正關(guān)閉的WebSocket連接可以同時由兩個端點初始化。WebSocket連接Close Code定義為包含在由實現(xiàn)該協(xié)議的應(yīng)用接收到的第一個Close控制幀的狀態(tài)碼(7.4節(jié))。如果這個Close控制幀不包含狀態(tài)碼,WebSocket連接Close Code被認為是1005。如果WebSocket連接已經(jīng)關(guān)閉且端點沒有接收到Close狀態(tài)碼(例如可能發(fā)生在底層傳輸連接丟失時),WebSocket連接Close Code被認為是1006。
注意:兩個端點可以有不一致的WebSocket連接關(guān)閉代碼。例如,如果遠程端點發(fā)送了一個Close幀,但本地應(yīng)用還沒有從它的socket接收緩沖區(qū)中讀到包含Close幀的數(shù)據(jù),且本地應(yīng)用獨立地決定關(guān)閉連接和發(fā)送一個Close幀,兩個端點都將發(fā)送和接收Close幀且將不發(fā)送更多的Close幀。每一個端點將看見另一端發(fā)送的以WebSocket連接關(guān)閉代碼結(jié)束的狀態(tài)碼。例如,在兩個端點獨立且在大致相同的時間同時開啟WebSocket關(guān)閉階段握手的情況下,兩個端點可以有不一致的WebSocket連接關(guān)閉代碼是可能的。
按照5.5.1和7.4節(jié)的定義,一個控Close控制幀可以包含一個指示關(guān)閉原因的狀態(tài)碼,接著是UTF-8編碼的數(shù)據(jù),上述數(shù)據(jù)留給斷點解釋且本協(xié)議沒有定義。WebSocket連接的關(guān)閉可以被任何一個端點初始化,可能同時發(fā)生。WebSocket 連接關(guān)閉原因由跟在包含在實現(xiàn)該協(xié)議的應(yīng)用接收到的第一個Close控制幀狀態(tài)碼(7.4節(jié))后邊的UTF-8編碼的數(shù)據(jù)定義。如果Close控制幀中沒有這樣的數(shù)據(jù),WebSocket連接關(guān)閉原因是空字符串。
注意:按照7.1.5節(jié)指出的相同的邏輯,兩個端點可以有不一致的WebSocket連接關(guān)閉原因。
某些算法和規(guī)范要求端點失敗WebSocket連接。要做到這一點,客戶端必須關(guān)閉WebSocket連接,并可以以適當?shù)姆绞桨褑栴}報告給用戶(這將對開發(fā)人員非常有用的)。
同樣的,為了做到這一點,服務(wù)器必須關(guān)閉WebSocket連接,并應(yīng)該記錄下問題。
如果已建立的WebSocket連接在端點需要失敗WebsSocket連接之前,端點應(yīng)該在處理關(guān)閉WebSocket連接之前發(fā)送一個帶有適當狀態(tài)碼的Close幀(7.4節(jié))。
如果端點認為另一邊不太可能收到并處理關(guān)閉幀可以省略發(fā)送一個關(guān)閉幀,因為錯誤的性質(zhì),導(dǎo)致WebSocket連接失敗擺在首要位置。端點必須在被指示為失敗WebSocket端點之后不繼續(xù)嘗試處理來自遠程端點的數(shù)據(jù)(包括響應(yīng)關(guān)閉幀)。
除上邊指出的或由應(yīng)用層指定的(例如,使用WebSocket API的腳本),客戶端應(yīng)該關(guān)閉連接。
某些算法,尤其在打開階段握手期間,需要客戶端失敗WebSocket連接。為了做到這一點,客戶端必須按照7.1.7節(jié)定義的那樣失敗WebSocket連接。
如果在任何時候,底層的傳輸層連接意外丟失,客戶端必須失敗WebSocket連接。 除上邊指出的或由應(yīng)用層指定的(例如,使用WebSocket API的腳本),客戶端應(yīng)該關(guān)閉連接。
某些算法需要或推薦服務(wù)端在打開階段握手期間中斷WebSocket連接。為了做到這一點,服務(wù)端必須簡單地關(guān)閉WebSocket連接(7.1.1節(jié))。
異常關(guān)閉可能由任何原因引起。這樣的關(guān)閉可能是一個瞬時錯誤導(dǎo)致的,在這種情況下重新連接可能導(dǎo)致一個好的連接和一個重新開始的正常操作。這樣的關(guān)閉也可能是一個非瞬時問題的導(dǎo)致的,在這種情況下如果每個部署的客戶端遇到異常關(guān)閉并立即且持續(xù)地的嘗試重新連接,服務(wù)端可能會因為大量的客戶端嘗試重新連接遇到的拒絕服務(wù)攻擊。這種情況的最終結(jié)果可能是服務(wù)不能及時的恢復(fù)或恢復(fù)是更加困難。
為了避免這個,當客戶端遇到本節(jié)描述的異常關(guān)閉之后嘗試重新連接時,應(yīng)該使用某種形式的補償。
第一個重新連接嘗試應(yīng)該延遲一個隨機的時間量。這種隨機延遲的參數(shù)的選擇留給客戶端決定;一個可隨機選擇的的值在0到5秒是一個合理的初始延遲,不過客戶端可以選擇不同的間隔由于其選擇一個延遲長度基于實現(xiàn)經(jīng)驗和特定的應(yīng)用。
第一次重新連接嘗試失敗,隨后的重新連接嘗試應(yīng)該延遲遞增的時間量,使用的方法如截斷二進制指數(shù)退避算法。
服務(wù)端在需要時可能關(guān)閉WebSocket連接??蛻舳瞬荒茈S意關(guān)閉WebSocket連接。在這兩種情況下,端點通過如下過程開始WebSocket關(guān)閉握手初始化一個關(guān)閉(7.1.2節(jié))。
當關(guān)閉一個已經(jīng)建立的連接(例如,當在打開階段握手已經(jīng)完成后發(fā)送一個關(guān)閉幀),端點可以表明關(guān)閉的原因。由端點解釋這個原因,并且端點應(yīng)該給這個原因采取動作,本規(guī)范是沒有定義的。本規(guī)范定義了一組預(yù)定義的狀態(tài)碼,并指定哪些范圍可以被擴展、框架和最終應(yīng)用使用。狀態(tài)碼和任何相關(guān)的文本消息是關(guān)閉幀的可選的組件。
當發(fā)送關(guān)閉幀時端點可以使用如下預(yù)定義的狀態(tài)碼。
1000
1000表示正常關(guān)閉,意思是建議的連接已經(jīng)完成了。
1001
1001表示端點“離開”(going away),例如服務(wù)器關(guān)閉或瀏覽器導(dǎo)航到其他頁面。
1002
1002表示端點因為協(xié)議錯誤而終止連接。
1003
1003表示端點由于它收到了不能接收的數(shù)據(jù)類型(例如,端點僅理解文本數(shù)據(jù),但接收到了二進制消息)而終止連接。
1004 保留。可能在將來定義其具體的含義。
1005
1005是一個保留值,且不能由端點在關(guān)閉控制幀中設(shè)置此狀態(tài)碼。它被指定用在期待一個用于表示沒有狀態(tài)碼是實際存在的狀態(tài)碼的應(yīng)用中。
1006
1006是一個保留值,且不能由端點在關(guān)閉控制幀中設(shè)置此狀態(tài)碼。它被指定用在期待一個用于表示連接異常關(guān)閉的狀態(tài)碼的應(yīng)用中。
1007
1007表示端點因為消息中接收到的數(shù)據(jù)是不符合消息類型而終止連接(比如,文本消息中存在非UTF-8[RFC3629]數(shù)據(jù))。
1008
1008表示端點因為接收到的消息違反其策略而終止連接。這是一個當沒有其他合適狀態(tài)碼(例如1003或1009)或如果需要隱藏策略的具體細節(jié)時能被返回的通用狀態(tài)碼。
1009
1009表示端點因接收到的消息對它的處理來說太大而終止連接。
1010
1010表示端點(客戶端)因為它期望服務(wù)器協(xié)商一個或多個擴展,但服務(wù)器沒有在WebSocket握手響應(yīng)消息中返回它們而終止連接。 所需要的擴展列表應(yīng)該出現(xiàn)在關(guān)閉幀的/reason/部分。
注意,這個狀態(tài)碼不能被服務(wù)器端使用,因為它可以失敗WebSocket握手。
1011
1011表示服務(wù)器端因為遇到了一個不期望的情況使它無法滿足請求而終止連接。
1015
1015是一個保留值,且不能由端點在關(guān)閉幀中被設(shè)置為狀態(tài)碼。它被指定用在期待一個用于表示連接由于執(zhí)行TLS握手失敗而關(guān)閉的狀態(tài)碼的應(yīng)用中(比如,服務(wù)器證書不能驗證)。
0-999
0-999范圍內(nèi)的狀態(tài)碼不被使用。
1000-2999
1000-2999范圍內(nèi)的狀態(tài)碼保留給本協(xié)議、其未來的修訂和一個永久的和現(xiàn)成的公共規(guī)范中指定的擴展的定義。
3000-3999
3000-3999范圍內(nèi)的狀態(tài)碼保留給庫、框架和應(yīng)用使用。這些狀態(tài)碼直接向IANA注冊。本規(guī)范未定義這些狀態(tài)碼的解釋。
4000-4999
4000-4999范圍內(nèi)的狀態(tài)碼保留用于私有使用且因此不能被注冊。這些狀態(tài)碼可以被在WebSocket應(yīng)用之間的先前的協(xié)議使用。本規(guī)范未定義這些狀態(tài)碼的解釋。
更多建議: