socket 及 websocket的握手過程

一、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是告訴服務器所使用的協議版本
OriginOrigin可以預防在瀏覽器中運行的腳本,在未經 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的那幾個函數中呢?請看下圖:

image

圖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中的四次握手釋放連接的過程,請看下圖:

image

圖2、socket中發送的TCP四次握手

圖示過程如下:

  • 某個應用進程首先調用close主動關閉連接,這時TCP發送一個FIN M;

  • 另一端接收到FIN M之後,執行被動關閉,對這個FIN進行確認。它的接收也作爲文件結束符傳遞給應用進程,因爲FIN的接收意味着應用進程在相應的連接上再也接收不到額外數據;

  • 一段時間之後,接收到文件結束符的應用進程調用close關閉它的socket。這導致它的TCP也發送一個FIN N;

  • 接收到這個FIN的源發送端TCP對它進行確認。

這樣每個方向上都有一個FIN和ACK。

參考:

https://www.jianshu.com/p/8414d2efa087

https://www.cnblogs.com/niwotaxuexiba/p/9700764.html

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