引言
在開發中,時不時會聽到關於HTTP的三次握手和四次揮手,在面試中也會被問道HTTP的三次握手和四次揮手,很多開發者可能都會有這這種誤解,認為三次握手和四次揮手都是HTTP協議的,實際上,這是錯誤的。正確的來說,三次揮手與四次握手是在TCP中進行的。
TCP中的三次握手
首先Client端傳送連線請求報文,Server段接受連線後回覆ACK報文,併為這次連線分配資源。Client端接收到ACK報文後也向Server段發生ACK報文,並分配資源,這樣TCP連線就建立了。
-
seq Sequence Number是傳送資料包中的第一個位元組的序列號,32位。
-
ack Acknowledgment Number是確認序列號,32位。
-
ACK 表示Acknowledgment Number欄位有意義
-
PSH 表示Push功能,RST表示復位TCP連線
-
SYN 表示SYN報文(在建立TCP連線的時候使用)
-
FIN 表示沒有資料需要傳送了(在關閉TCP連線的時候使用)
-
Source Port是源埠,16位。
-
Destination Port是目的埠,16位。
-
Data Offset是資料偏移,4位,該欄位的值是TCP首部(包括選項)長度除以4。 [1]
-
標誌位: 6位,URG表示Urgent Pointer欄位有意義: 通過下面的圖片,我們來詳細分析下TCP三次握手
(1)最初兩端的TCP程式都處於CLOSED關閉狀態,A(Client)主動開啟連線,而B(Server)被動開啟連線。(A、B關閉狀態CLOSED——B收聽狀態LISTEN——A同步已傳送狀態SYN-SENT——B同步收到狀態SYN-RCVD——A、B連線已建立狀態ESTABLISHED)
- 第一次握手:起初兩端都處於CLOSED關閉狀態,A(Client)將標誌位SYN置為1,隨機產生一個值seq=x,並將該資料包傳送給B(Server),A(Client)進入SYN-SENT狀態,等待B(Server)確認;
- 第二次握手:B(Server)收到連線請求報文段後,如同意建立連線,則向A(Client)傳送確認,在確認報文段中(SYN=1,ACK=1,確認號ack=x+1,初始序號seq=y),B(Server)TCP伺服器程式進入SYN-RCVD(同步收到)狀態;
- 第三次握手:TCP客戶程式收到B(Server)的確認後,要向B(Server)給出確認報文段(ACK=1,確認號ack=y+1,序號seq=x+1)(初始為seq=x,第二個報文段所以要+1),ACK報文段可以攜帶資料,不攜帶資料則不消耗序號。TCP連線已經建立,A進入ESTABLISHED(已建立連線)。
- 當B收到A的確認後,也進入ESTABLISHED狀態。
(2)總結三次握手過程:
- 第一次握手:起初兩端都處於CLOSED關閉狀態,Client將標誌位SYN置為1,隨機產生一個值seq=x,並將該資料包傳送給Server,Client進入SYN-SENT狀態,等待Server確認;
- 第二次握手:Server收到資料包後由標誌位SYN=1得知Client請求建立連線,Server將標誌位SYN和ACK都置為1,ack=x+1,隨機產生一個值seq=y,並將該資料包傳送給Client以確認連線請求,Server進入SYN-RCVD狀態,此時作業系統為該TCP連線分配TCP快取和變數;
- 第三次握手:Client收到確認後,檢查ack是否為x+1,ACK是否為1,如果正確則將標誌位ACK置為1,ack=y+1,並且此時作業系統為該TCP連線分配TCP快取和變數,並將該資料包傳送給Server,Server檢查ack是否為y+1,ACK是否為1,如果正確則連線建立成功,Client和Server進入ESTABLISHED狀態,完成三次握手,隨後Client和Server就可以開始傳輸資料。
起初A和B都處於CLOSED狀態——B建立TCB,處於LISTEN狀態,等待A請求——A建立TCB,傳送連線請求(SYN=1,seq=x),進入SYN-SENT狀態——B收到連線請求,向A傳送確認(SYN=ACK=1,確認號ack=x+1,初始序號seq=y),進入SYN-RCVD狀態——A收到B的確認後,給B發出確認(ACK=1,ack=y+1,seq=x+1),A進入ESTABLISHED狀態——B收到A的確認後,進入ESTABLISHED狀態。
TCB傳輸控制塊Transmission Control Block,儲存每一個連線中的重要資訊,如TCP連線表,到傳送和接收快取的指標,到重傳佇列的指標,當前的傳送和接收序號。
(3)為什麼A還要傳送一次確認呢?可以二次握手嗎?
- **主要為了防止已失效的連線請求報文段突然又傳送到了B,因而產生錯誤。**如A發出連線請求,但因連線請求報文丟失而未收到確認,於是A再重傳一次連線請求。後來收到了確認,建立了連線。資料傳輸完畢後,就釋放了連線,A發出了兩個連線請求報文段,其中第一個丟失,第二個到達了B,但是第一個丟失的報文段只是在某些網路結點長時間滯留了,延誤到連線釋放以後的某個時間才到達B,此時B誤認為A又發出一次新的連線請求,於是就向A發出確認報文段,同意建立連線,不採用三次握手,只要B發出確認,就建立新的連線了,此時A不理睬B的確認且不傳送資料,則B一致等待A傳送資料,浪費資源。
(4)Server端易受到SYN攻擊?
-
伺服器端的資源分配是在二次握手時分配的,而客戶端的資源是在完成三次握手時分配的,所以伺服器容易受到SYN洪泛攻擊,SYN攻擊就是Client在短時間內偽造大量不存在的IP地址,並向Server不斷地傳送SYN包,Server則回覆確認包,並等待Client確認,由於源地址不存在,因此Server需要不斷重發直至超時,這些偽造的SYN包將長時間佔用未連線佇列,導致正常的SYN請求因為佇列滿而被丟棄,從而引起網路擁塞甚至系統癱瘓。
-
防範SYN攻擊措施:降低主機的等待時間使主機儘快的釋放半連線的佔用,短時間受到某IP的重複SYN則丟棄後續請求。
TCP中的四次揮手
2、四次揮手
(1)四次揮手的詳述
假設Client端發起中斷連線請求,也就是傳送FIN報文。Server端接到FIN報文後,意思是說"我Client端沒有資料要發給你了",但是如果你還有資料沒有傳送完成,則不必急著關閉Socket,可以繼續傳送資料。所以你先傳送ACK,"告訴Client端,你的請求我收到了,但是我還沒準備好,請繼續你等我的訊息"。這個時候Client端就進入FIN_WAIT狀態,繼續等待Server端的FIN報文。當Server端確定資料已傳送完成,則向Client端傳送FIN報文,"告訴Client端,好了,我這邊資料發完了,準備好關閉連線了"。Client端收到FIN報文後,"就知道可以關閉連線了,但是他還是不相信網路,怕Server端不知道要關閉,所以傳送ACK後進入TIME_WAIT狀態,如果Server端沒有收到ACK則可以重傳。“,Server端收到ACK後,"就知道可以斷開連線了"。Client端等待了2MSL後依然沒有收到回覆,則證明Server端已正常關閉,那好,我Client端也可以關閉連線了。Ok,TCP連線就這樣關閉了! 通過下面的圖片,我們來詳細分析下TCP三次握手
資料傳輸結束後,通訊的雙方都可釋放連線,A和B都處於ESTABLISHED狀態。(A、B連線建立狀態ESTABLISHED——A進入等待1狀態FIN-WAIT-1——B關閉等待狀態CLOSE-WAIT——A進入等待2狀態FIN-WAIT-2——B最後確認狀態LAST-ACK——A時間等待狀態TIME-WAIT——B、A關閉狀態CLOSED)
- A的應用程式先向其TCP發出連線釋放報文段(FIN=1,序號seq=u),並停止再傳送資料,主動關閉TCP連線,進入FIN-WAIT-1(終止等待1)狀態,等待B的確認。
- B收到連線釋放報文段後即發出確認報文段,(ACK=1,確認號ack=u+1,序號seq=v),B進入CLOSE-WAIT(關閉等待)狀態,此時的TCP處於半關閉狀態,A到B的連線釋放。
- A收到B的確認後,進入FIN-WAIT-2(終止等待2)狀態,等待B發出的連線釋放報文段。
- B沒有要向A發出的資料,B發出連線釋放報文段(FIN=1,ACK=1,序號seq=w,確認號ack=u+1),B進入LAST-ACK(最後確認)狀態,等待A的確認。
- A收到B的連線釋放報文段後,對此發出確認報文段(ACK=1,seq=u+1,ack=w+1),A進入TIME-WAIT(時間等待)狀態。此時TCP未釋放掉,需要經過時間等待計時器設定的時間2MSL後,A才進入CLOSED狀態。
(2)總結四次揮手過程:
- 起初A和B處於ESTABLISHED狀態——A發出連線釋放報文段並處於FIN-WAIT-1狀態——B發出確認報文段且進入CLOSE-WAIT狀態——A收到確認後,進入FIN-WAIT-2狀態,等待B的連線釋放報文段——B沒有要向A發出的資料,B發出連線釋放報文段且進入LAST-ACK狀態——A發出確認報文段且進入TIME-WAIT狀態——B收到確認報文段後進入CLOSED狀態——A經過等待計時器時間2MSL後,進入CLOSED狀態。
(3)為什麼A在TIME-WAIT狀態必須等待2MSL的時間?(MSL最長報文段壽命Maximum Segment Lifetime,MSL=2)
-
原因:1)**保證A傳送的最後一個ACK報文段能夠到達B。**這個ACK報文段有可能丟失,使得處於LAST-ACK狀態的B收不到對已傳送的FIN+ACK報文段的確認,B超時重傳FIN+ACK報文段,而A能在2MSL時間內收到這個重傳的FIN+ACK報文段,接著A重傳一次確認,重新啟動2MSL計時器,最後A和B都進入到CLOSED狀態,若A在TIME-WAIT狀態不等待一段時間,而是傳送完ACK報文段後立即釋放連線,則無法收到B重傳的FIN+ACK報文段,所以不會再傳送一次確認報文段,則B無法正常進入到CLOSED狀態。
-
原因:2)防止“已失效的連線請求報文段”出現在本連線中。A在傳送完最後一個ACK報文段後,再經過2MSL,就可以使本連線持續的時間內所產生的所有報文段都從網路中消失,使下一個新的連線中不會出現這種舊的連線請求報文段。
(4)為什麼連線的時候是三次握手,關閉的時候卻是四次握手?
- 因為當Server端收到Client端的SYN連線請求報文後,可以直接傳送SYN+ACK報文。其中ACK報文是用來應答的,SYN報文是用來同步的。但是關閉連線時,當Server端收到FIN報文時,很可能並不會立即關閉SOCKET,所以只能先回復一個ACK報文,告訴Client端,"你發的FIN報文我收到了"。只有等到我Server端所有的報文都傳送完了,我才能傳送FIN報文,因此不能一起傳送。故需要四步握手。
(5)為什麼TIME_WAIT狀態需要經過2MSL(最大報文段生存時間)才能返回到CLOSE狀態?
- 雖然按道理,四個報文都傳送完畢,我們可以直接進入CLOSE狀態了,但是我們必須假象網路是不可靠的,有可能最後一個ACK丟失。所以TIME_WAIT狀態就是用來重發可能丟失的ACK報文。
(6)優化,我們可以通過修改系統引數來優化伺服器
- tcp_tw_reuse: 是否重用處於TIME_WAIT狀態的TCP連結 (設為true)
- tcp_max_tw_buckets: 處於TIME_WAIT狀態的SOCKET最大數目 (調大,這個引數千萬不要調小了)
- tcp_fin_timeout: 處於FIN_WAIT_2的時間 (調小)