天天看點

Linux網絡程式設計——淺談 TCP 三次握手和四次揮手

三次握手

在 TCP/IP 協定中,TCP 協定提供可靠的連接配接服務,采用​​三次握手​​建立一個連接配接。

Linux網絡程式設計——淺談 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 狀态,完成三次握手。

Linux網絡程式設計——淺談 TCP 三次握手和四次揮手

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

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

第一次對話: 

老婆讓甲出去打醬油,半路碰到一個朋友乙,甲問了一句:哥們你吃飯了麼? 

結果乙帶着耳機聽歌呢,根本沒聽到,沒反應。甲心裡想:跟你說話也沒個音,不跟你說了,溝通失敗。說明乙接受不到甲傳過來的資訊的情況下溝通肯定是失敗的。

如果乙聽到了甲說的話,那麼第一次對話成功,接下來進行第二次對話。 

第二次對話: 

乙聽到了甲說的話,但是他是老外,中文不好,不知道甲說的啥意思也不知道怎樣回答,于是随便回答了一句學過的中文 :我去廁所了。甲一聽立刻笑噴了,“去廁所吃飯”?道不同不相為謀,離你遠點吧,溝通失敗。說明乙無法做出正确應答的情況下溝通失敗。 

如果乙聽到了甲的話,做出了正确的應答,并且還進行了反問:我吃飯了,你呢?那麼第二次握手成功。 

通過前兩次對話證明了乙能夠聽懂甲說的話,并且能做出正确的應答。接下來進行第三次對話。 

第三次對話: 

甲剛和乙打了個招呼,突然老婆喊他,“你個死鬼,打個醬油咋這麼半天,看我回家咋收拾你”,甲是個妻管嚴,聽完吓得二話不說就跑回家了,把乙自己晾那了。乙心想:這什麼人啊,得,我也回家吧,溝通失敗。說明甲無法做出應答的情況下溝通失敗。 

如果甲也做出了正确的應答:我也吃了。那麼第三次對話成功,兩人已經建立起了順暢的溝通管道,接下來開始持續的聊天。

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

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

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

四次揮手

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

Linux網絡程式設計——淺談 TCP 三次握手和四次揮手

那​​TCP 的四次握手​​,是為了保證通信雙方都關閉了連接配接,具體過程如下:

Linux網絡程式設計——淺談 TCP 三次握手和四次揮手

1)用戶端 A 發送一個 FIN,用來關閉客戶 A 到伺服器 B 的資料傳送;

2)伺服器 B 收到這個 FIN,它發回一個 ACK,确認序号為收到的序号加 1。和 SYN 一樣,一個 FIN 将占用一個序号;

3)伺服器 B 關閉與用戶端 A 的連接配接,發送一個 FIN 給用戶端 A;

4)用戶端 A 發回 ACK 封包确認,并将确認序号設定為收到序号加 1。

Linux網絡程式設計——淺談 TCP 三次握手和四次揮手

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

這是因為服務端的 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 通信過程中各步驟的狀态》​​)

繼續閱讀