TCP三次握手/四次揮手及其狀態分析
前面CLOSE_WAIT狀態分析與TIME_WAIT狀態分析其實都是TCP斷開連線過程中的兩個狀態.本文繼續介紹下TCP連線三次握手,四次揮手的過程及其中間的狀態
三次握手原理:
第一次握手:客戶端傳送syn包(syn=j)到伺服器,等待伺服器確認.
第二次握手:伺服器收到syn包,必須確認(ack=j+1),同時自己傳送一個syn包(syn=k),即SYN+ACK包.
第三次握手:客戶端收到伺服器的SYN+ACK包,向伺服器傳送確認包(ack=k+1).
圖示:
CLINET ----- SYN(j) ----- SERVER
SERVER ----- ACK(j+1) SYN(k) ----- CLINET
CLINET ----- ACK(k+1) ----- SERVER
客戶端傳送syn包,進入SYN_SEND狀態,伺服器收到sync包,進入SYN_RECV狀態.
客戶端收到伺服器端的SYN+ACK包進入ESTABLISHED狀態.
伺服器端收到客戶機端的ack包,進入ESTABLISHED狀態.
簡單的說:
客戶端問伺服器:你允許我和你建立連線麼?
伺服器端說:可以,那你也允許我和你建立連線麼?
客戶端說:可以.
於是,他們之間就建立起連線了.
四次分手原理:
由於TCP連線是全雙工的,因此每個方向都必須單獨進行關閉.這個原則是當一方完成它的資料傳送任務後就能傳送一個FIN來終止這個方向的連線.收到一個 FIN只意味著這一方向上沒有資料流動,一個TCP連線在收到一個FIN後仍能傳送資料.首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉.
(1)客戶端A傳送一個FIN,用來關閉客戶A到伺服器B的資料傳送.
(2)伺服器B收到這個FIN,它發回一個ACK,確認序號為收到的序號加1.
(3)伺服器B關閉與客戶端A的連線,傳送一個FIN給客戶端A.
(4)客戶端A發回ACK報文確認,並將確認序號設定為收到序號加1.
圖示:
CLINET --- FIN --- SERVER
SERVER --- ACK --- CLINET
SERVER --- FIN --- CLINET
CLINET --- ACK --- SERVER
客戶端主動關閉連線,傳送FIN包,進入FIN_WAIT_1狀態.接收端收到FIN包,進入CLOSE_WAIT狀態.
客戶端收到伺服器的ACK包,進入FIN_WAIT_2狀態.
伺服器傳送FIN包,進入LAST_ACK狀態.客戶端接收到FIN包,進入TIME_WAIT狀態
伺服器收到ACK包,進入CLOSED狀態.
簡單的說:
客戶端問伺服器:我們斷開連線吧?
伺服器端說:可以,我將不再接收你的資料.
伺服器端問伺服器:我將要跟你斷開連線了,可以吧?
客戶端說:可以.
客戶端說:可以.
於是,他們之間的連線就斷開了.
TCP連線正常建立和終止過程及對應狀態如圖:
這裡有個連線同時開啟的問題,跟前面文章中說的五元組問題一樣,很難出現,但如果指定本地和對方兩端的埠號還是能製造出這樣的情況.
兩端都是在SYN_SENT狀態下收到對方的SYN包而進入SYN_RCVD狀態,然後都會發送SYN+ACK包,當兩端都會收到該包時,狀態會變成ESTABLISHED.
圖示:
CLINET --- SYN --- SERVER SERVER --- SYN --- CLINET
SERVER --- SYN+ACK --- CLINET CLINET --- SYN+ACK --- SERVER
TCP連線同時建立過程及對應狀態如圖:
繼續說下CLOSING狀態,當兩端幾乎同時關閉連線時,兩端都是在FIN_WAIT_1狀態下收到對方的FIN包,會進入CLOSING狀態.然後都會發送最後的ACK包.當兩端都會收到該包時,狀態會變成TIME_WAIT.
圖示:
CLINET --- FIN --- SERVER SERVER --- FIN --- CLINET
SERVER --- ACK --- CLINET CLINET --- ACK --- SERVER
TCP連線同時關閉過程及對應狀態如圖:
還有個問題:為什麼建立連線協議是三次握手,而關閉連線卻是四次握手呢?
這是因為服務端的LISTEN狀態下的SOCKET當收到SYN報文的建連請求後,它可以把ACK和SYN(ACK起應答作用,而SYN起同步作用)放在一個報文裡來發送.但關閉連線時,當收到對方的FIN報文通知時,它僅僅表示對方沒有資料傳送給你了.但未必你所有的資料都全部發送給對方了,所以你可以未必會馬上會關閉SOCKET,也即你可能還需要傳送一些資料給對方之後,再發送FIN報文給對方來表示你同意現在可以關閉連線了,所以它這裡的ACK報文和FIN報文多數情況下都是分開發送的.