一、WebSocket和HTTP之間的關係
WebSocket和HTTP一樣都是基於TCP的應用層協議。
WebSocket協議和HTTP協議是兩種不同的東西。客戶端開始建立WebSocket連接時要發送一個header標記了 Upgrade的HTTP請求,表示請求協議升級。所以服務器端做出響應的簡便方法是,直接在現有的HTTP服務器軟件和現有的端口上實現WebSocket協議,然後再回一個狀態碼爲101的HTTP響應完成握手,再往後發送數據時就沒 HTTP的事了。也就是說WebSocket只是使用HTTP協議來完成一部分握手。
二、握手
握手部分的設計目的就是兼容現有的基於HTTP的服務端組件(web服務器軟件)或者中間件(代理服務器軟件)。這樣一個端口就可以同時接受普通的HTTP請求或則WebSocket請求了。爲了這個目的,WebSocket客戶端的握手是一個 HTTP升級版的請求(HTTP Upgrade request):
客戶端發送一個請求
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
這段類似HTTP協議的握手請求中多了以下幾個東西
Upgrade: websocket
Connection: Upgrade
是告訴服務器我想升級爲websocket協議
Sec-WebSocket-Key
是一個Base64
加密的密鑰Sec-WebSocket-Protocol
是用於標識客戶端想和服務端使用哪一種子協議(都是應用層的協議,比如 chat 表示採用 “聊天” 這個應用層協議)。Sec-WebSocket-Version
是告訴服務器所使用的協議版本Origin
Origin可以預防在瀏覽器中運行的腳本,在未經 WebSocket 服務器允許的情況下,對其發送跨域的請求。瀏覽器腳本在使用瀏覽器提供的 WebSocket 接口對一個 WebSocket 服務發起連接請求時,瀏覽器會在請求的 Origin 中標識出發出請求的腳本所屬的源,然後 WebSocket 在接受到瀏覽器的連接請求之後,就可以根據其中的源去選擇是否接受當前的請求。
比如我們有一個 WebSocket 服務運行在 http://websocket.example.com,然後你打開一個網頁 http://another.example.com,在個 another 的頁面中,有一段腳本試圖向我們的 WebSocket 服務發起鏈接,那麼瀏覽器在其請求的頭中,就會標註請求的源爲 http://another.example.com,這樣我們就可以在自己的服務中選擇接收或者拒絕該請求。
服務端響應
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
上面提到了這個101
是狀態碼,websocket是使用 HTTP 協議的101狀態碼進行協議切換。
Sec-WebSocket-Accept
這個值表示服務器同意握手建立連接,是客戶端傳輸過來的Sec-WebSocket-Key跟“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”拼接後,用SHA-1加密,並進行BASE-64編碼得來的,Sha1(Sec-WebSocket-Key+258EAFA5-E914-47DA-95CA-C5AB0DC85B11)。
客戶端收到Sec-WebSocket-Accept後,將本地的Sec-WebSocket-Key進行同樣的編碼,然後比對。
三、socket中TCP的三次握手建立連接詳解
我們知道tcp建立連接要進行“三次握手”,即交換三個分組。大致流程如下:
- 客戶端向服務器發送一個SYN J
- 服務器向客戶端響應一個SYN K,並對SYN J進行確認ACK J+1
- 客戶端再想服務器發一個確認ACK K+1
只有就完了三次握手,但是這個三次握手發生在socket的那幾個函數中呢?請看下圖:
圖1、socket中發送的TCP三次握手
從圖中可以看出,當客戶端調用connect時,觸發了連接請求,向服務器發送了SYN J包,這時connect進入阻塞狀態;服務器監聽到連接請求,即收到SYN J包,調用accept函數接收請求向客戶端發送SYN K ,ACK J+1,這時accept進入阻塞狀態;客戶端收到服務器的SYN K ,ACK J+1之後,這時connect返回,並對SYN K進行確認;服務器收到ACK K+1時,accept返回,至此三次握手完畢,連接建立。
總結:客戶端的connect在三次握手的第二個次返回,而服務器端的accept在三次握手的第三次返回。
四、socket中TCP的四次握手釋放連接詳解
上面介紹了socket中TCP的三次握手建立過程,及其涉及的socket函數。現在我們介紹socket中的四次握手釋放連接的過程,請看下圖:
圖2、socket中發送的TCP四次握手
圖示過程如下:
-
某個應用進程首先調用close主動關閉連接,這時TCP發送一個FIN M;
-
另一端接收到FIN M之後,執行被動關閉,對這個FIN進行確認。它的接收也作爲文件結束符傳遞給應用進程,因爲FIN的接收意味着應用進程在相應的連接上再也接收不到額外數據;
-
一段時間之後,接收到文件結束符的應用進程調用close關閉它的socket。這導致它的TCP也發送一個FIN N;
-
接收到這個FIN的源發送端TCP對它進行確認。
這樣每個方向上都有一個FIN和ACK。
參考: