1. 程式人生 > >《TCP/IP協議族》:TCP 三次握手和四次揮手

《TCP/IP協議族》:TCP 三次握手和四次揮手

1. 三次握手

在 TCP/IP 協議中,TCP 協議提供可靠的連線服務,採用三次握手建立一個連線。

第一次握手:建立連線時,客戶端傳送 syn 包(syn=j)到伺服器,並進入 SYN_SEND 狀態,等待伺服器確認; 

第二次握手:伺服器收到 syn 包,必須確認客戶的 SYN(ack=j+1),同時自己也傳送一個 SYN 包(syn=k),即 SYN+ACK 包,此時伺服器進入 SYN_RECV 狀態; 

第三次握手:客戶端收到伺服器的 SYN+ACK 包,向伺服器傳送確認包 ACK(ack=k+1),此包傳送完畢,客戶端和伺服器進入 ESTABLISHED 狀態,完成三次握手。

通過這樣的三次握手,客戶端與服務端建立起可靠的雙工的連線,開始傳送資料。 三次握手的最主要目的是保證連線是雙工的,可靠更多的是通過重傳機制來保證的但是為什麼一定要進行三次握手來保證連線是雙工的呢,一次不行麼?兩次不行麼?

我們舉一個現實生活中兩個人進行語言溝通的例子來模擬三次握手

第一次對話: 
老婆讓甲出去打醬油,半路碰到一個朋友乙,甲問了一句:哥們你吃飯了麼? 
結果乙帶著耳機聽歌呢,根本沒聽到,沒反應。甲心裡想:跟你說話也沒個音,不跟你說了,溝通失敗。說明乙接受不到甲傳過來的資訊的情況下溝通肯定是失敗的。
如果乙聽到了甲說的話,那麼第一次對話成功,接下來進行第二次對話。 


第二次對話: 
乙聽到了甲說的話,但是他是老外,中文不好,不知道甲說的啥意思也不知道怎樣回答,於是隨便回答了一句學過的中文 :我去廁所了。甲一聽立刻笑噴了,“去廁所吃飯”?道不同不相為謀,離你遠點吧,溝通失敗。說明乙無法做出正確應答的情況下溝通失敗。 
如果乙聽到了甲的話,做出了正確的應答,並且還進行了反問:我吃飯了,你呢?那麼第二次握手成功。 
通過前兩次對話證明了乙能夠聽懂甲說的話,並且能做出正確的應答。接下來進行第三次對話。 


第三次對話: 
甲剛和乙打了個招呼,突然老婆喊他,“你個死鬼,打個醬油咋這麼半天,看我回家咋收拾你”,甲是個妻管嚴,聽完嚇得二話不說就跑回家了,把乙自己晾那了。乙心想:這什麼人啊,得,我也回家吧,溝通失敗。說明甲無法做出應答的情況下溝通失敗。 
如果甲也做出了正確的應答:我也吃了。那麼第三次對話成功,兩人已經建立起了順暢的溝通渠道,接下來開始持續的聊天。 


通過第二次和第三次的對話證明了甲能夠聽懂乙說的話,並且能做出正確的應答。 可見,兩個人進行有效的語言溝通,這三次對話的過程是必須的。

同理對於TCP為什麼需要進行三次握手我們可以一樣的理解: 

為了保證服務端能收接受到客戶端的資訊並能做出正確的應答而進行前兩次(第一次和第二次)握手,為了保證客戶端能夠接收到服務端的資訊並能做出正確的應答而進行後兩次(第二次和第三次)握手。 

 

2. 四次揮手

由於 TCP 連線是全雙工的,因此每個方向都必須單獨進行關閉。這好比,我們打電話(全雙工),正常的情況下(出於禮貌),通話的雙方都要說再見後才能掛電話,保證通訊雙方都把話說完了才掛電話

TCP 的四次握手是為了保證通訊雙方都關閉了連線,具體過程如下:

1)客戶端 A 傳送一個 FIN,用來關閉客戶 A 到伺服器 B 的資料傳送;
2)伺服器 B 收到這個 FIN,它發回一個 ACK,確認序號為收到的序號加 1。和 SYN 一樣,一個 FIN 將佔用一個序號;
3)伺服器 B 關閉與客戶端 A 的連線,傳送一個 FIN 給客戶端 A;
4)客戶端 A 發回 ACK 報文確認,並將確認序號設定為收到序號加 1。

為什麼建立連線協議是三次握手,而關閉連線卻是四次握手呢?

這是因為服務端的 LISTEN 狀態下的 SOCKET 當收到 SYN 報文的建連請求後,它可以把 ACK 和 SYN(ACK 起應答作用,而 SYN 起同步作用)放在一個報文裡來發送。但關閉連線時,當收到對方的 FIN 報文通知時,它僅僅表示對方沒有資料傳送給你了,但是你還可以給對方傳送資料,也有這麼種可能,你還有一些資料在傳給對方的途中,所以你不能立馬關閉連線,也即你可能還需要把在傳輸途中的資料給對方之後,又或者,你還有一些資料需要傳輸給對方後,(再關閉連線)再發送FIN 報文給對方來表示你同意現在可以關閉連線了,所以它這裡的 ACK 報文和 FIN 報文多數情況下都是分開發送的。

為什麼 TIME_WAIT 狀態還需要等 2MS L後才能返回到 CLOSED 狀態?

這是因為雖然雙方都同意關閉連線了,而且握手的 4 個報文也都協調和傳送完畢,按理可以直接回到 CLOSED 狀態(就好比從 SYN_SEND 狀態到 ESTABLISH 狀態那樣);但是因為我們必須要假想網路是不可靠的,你無法保證你最後傳送的 ACK 報文會一定被對方收到,因此對方處於 LAST_ACK 狀態下的 SOCKET 可能會因為超時未收到 ACK 報文,而重發 FIN 報文,所以這個 TIME_WAIT 狀態的作用就是用來重發可能丟失的 ACK 報文。(裡面涉及的狀態是什麼意思,詳情請看《TCP 通訊過程中各步驟的狀態