你好,我是看山。
一直聽别人說 HTTP 長連接配接,隻知道長連接配接比短連接配接更節省資源、更快捷,但是并不真的知道原因。知其然不知其是以然,對于技術來說,這種狀态是比較危險的。是以,還是要挖一下原理,即使挖的比較淺,也要邁出這一步。
HTTP 是應用層協定,傳輸層使用的是 TCP 協定,網絡層使用的是 IP 協定。
IP 協定主要解決網絡路由和尋址問題,TCP 協定主要解決如何在 IP 層之上可靠的傳遞資料包,使在網絡上的另一端收到發送端發出的所有包,并且順序與發出順序一緻,HTTP 協定主要基于 TCP 協定完成資料傳遞。
首先說下 TCP 連接配接與斷開。
TCP 的生命周期被戲稱為三次握手和四次揮手,每次握手(揮手)都需要通信雙方互動資料,是以 TCP 連接配接傳輸資料是比較耗費資源的,也就是通常所說的成本較高。
然後,HTTP 協定是無狀态的、面向連接配接的,即協定本身不具備記憶能力,兩次不同的 HTTP 請求之間沒有任何聯系。
在 HTTP/1.1 之前,一個網頁加載資源的時候,每需要加載一個資源,就需要進行一次 HTTP 請求,建立一次連接配接,請求結束就斷開連接配接,而每次連接配接(TCP 連接配接)都需要耗費資源(時間資源、網絡資源等)。
是以後來在 HTTP/1.1 中引入持久連接配接(persistent connection),即 TCP 連接配接預設不關閉,可以被多個請求複用,不用聲明Connection: keep-alive,也就是常說的長連接配接。
這裡所說的長連接配接,其實本身是 TCP 長連接配接。比如發起一次 HTTP 請求時,用戶端與服務端建立 TCP 連接配接,在得到響應結果後,不進行 TCP 的四次揮手斷開連接配接,而是會保持一段時間的 TCP 連接配接。此時,如果又有一次 HTTP 請求相同的服務端,就會繼續使用這一個 TCP 連接配接。這樣,節省了 TCP 連接配接的消耗。
當然,為了資源的有效利用,在一段時間(逾時時間,請求頭中Keep-alive: timeout=10進行設定)沒有活動時,用戶端或服務端會主動斷開 TCP 連接配接。建議的做法是,在用戶端最後一次請求時,請求頭中發送Connection: close,明确要求關閉 TCP 連接配接。
補充下 TCP 三次握手、四次揮手的知識。
三次握手建立連接配接:
第一次握手:用戶端發送 SYN 包 (seq=x) 到伺服器,并進入 SYN_SEND 狀态,等待伺服器确認;
第二次握手:伺服器收到 SYN 包,必須确認客戶的 SYN(ACK=x+1),同時自己也發送一個 SYN 包(seq=y),即 SYN+ACK 包,此時伺服器進入 SYN_RECV 狀态;
第三次握手:用戶端收到伺服器的 SYN+ACK 包,向伺服器發送确認包 ACK(ACK=y+1),此包發送完畢,用戶端和伺服器進入 ESTABLISHED 狀态,完成三次握手。
握手過程中傳送的包裡不包含資料,三次握手完畢後,用戶端與伺服器才正式開始傳送資料。理想狀态下,TCP 連接配接一旦建立,在通信雙方中的任何一方主動關閉連接配接之前,TCP 連接配接都将被一直保持下去。
四次揮手斷開連接配接
第一次揮手:主動關閉方發送一個 FIN,用來關閉主動方到被動關閉方的資料傳送,也就是主動關閉方告訴被動關閉方:我已經不會再給你發資料了(當然,在 FIN 包之前發送出去的資料,如果沒有收到對應的 ACK 确認封包,主動關閉方依然會重發這些資料),但此時主動關閉方還可以接受資料。
第二次揮手:被動關閉方收到 FIN 包後,發送一個 ACK 給對方,确認序号為收到序号+1(與 SYN 相同,一個 FIN 占用一個序号)。
第三次揮手:被動關閉方發送一個 FIN,用來關閉被動關閉方到主動關閉方的資料傳送,也就是告訴主動關閉方,我的資料也發送完了,不會再給你發資料了。
第四次揮手:主動關閉方收到 FIN 後,發送一個 ACK 給被動關閉方,确認序号為收到序号+1,至此,完成四次揮手。
注意:中斷連接配接端可以是 Client 端,也可以是 Server 端。
詳細的狀态說明
SYN_SEND:用戶端嘗試連結服務端,通過 open 方法。也就是 TCP 三次握手中的第 1 步之後,注意是用戶端狀态
SYN_RECEIVED: 服務接受建立請求的 SYN 後,也就是 TCP 三次握手中的第 2 步,發送 ACK 資料包之前。注意是服務端狀态,一般 15 個左右正常,如果很大,懷疑遭受 SYN_FLOOD 攻擊
ESTABLISHED:用戶端接受到服務端的 ACK 包後的狀态,服務端在發出 ACK 在一定時間後即為 ESTABLISHED
FIN_WAIT1:主動關閉的一方,在發出 FIN 請求之後,也就是在 TCP 四次揮手的第 1 步
CLOSE_WAIT:被動關閉的一方,在接受到用戶端的 FIN 後,也就是在 TCP 四次揮手的第 2 步
FIN_WAIT2:主動關閉的一方,在接受到被動關閉一方的 ACK 後,也就是 TCP 四次揮手的第 2 步。可以設定被動關閉方傳回 FIN 後的逾時時間,有效回收連結,避免 syn-flood.
LASK_ACK:被動關閉的一方,在發送 ACK 後一段時間後(確定用戶端已收到),再發起一個 FIN 請求。也就是 TCP 四次揮手的第 3 步
TIME_WAIT:主動關閉的一方,在收到被動關閉的 FIN 包後,發送 ACK。也就是 TCP 四次揮手的第 4 步