計算機網絡——TCP的三次握手與四次揮手(超詳細)


一、前言

  今天剛看完《計算機網絡——自頂向下方法》這本書的運輸層這一章。直到今天我才知道,TCP協議居然有這么復雜(之前上課都沒怎么認真聽),這一章節總共七十多頁,主要介紹UDPTCP,但UDP的內容卻只占不超過5頁,大部分的篇幅都是在講解TCP的機制。但是就算這樣,我還是感覺這本書對TCP的講解不夠詳細,忽略了很多內容和細節,導致我無法將這本書對TCP的講解串通在一起,感覺前后矛盾,同時也有很多地方無法理解。這一章讀下來,我對TCP有了更深入的了解,但是同時也對它有了更多的疑問。由此看來,想要真正弄懂TCP,不能僅僅依靠這種入門書籍,應該選擇一些專門講解這一塊內容的書籍進行研究。

  接下來我准備將這幾天看的TCP的內容進行一次總結,以博客的形式將這些內容記錄下來,算是加深印象吧。這篇博客就先來說一說TCP三次握手四次揮手,也就是TCP連接的建立與斷開,算是TCP中相對比較簡單的部分。


二、解析

 2.1 TCP的報文格式

  想要了解TCP的三次握手與四次揮手,首先得了解一下TCP的報文格式,因為這兩個過程需要依賴TCP報文中的一些字段。下面我們通過一幅圖來講解TCP的報文格式:

  我只介紹TCP報文中,與三次握手和四次揮手有關的部分:

  • 源端口號:本次TCP連接中,發起連接的主機使用的端口號;
  • 目的端口號:本次TCP連接主,接受連接的主機使用的端口號;
  • 序號:通過TCP傳輸的每一個數據段,都有一個序號,作用是為了確認此數據段的順序。網絡中允許傳輸的數據長度是有限制的,所以當我們要通過TCP傳輸一個較大的數據時,TCP會將數據切割成很多小的數據段進行傳輸。而將這些小的數據段發送到目的主機時(發送方會同時發送多個數據),並不能保證它們是按順序到達目的地,所以對於每一個數據段,都要有一個序號,來標識它們是屬於總數據的哪一部分,以保證在目的主機中能將他們重新拼接。
  • 確認序號:接收方若接收到一個數據段,會發送一個確認報文給發送方,告訴發送方已經接收到這個數據段,而確認序號的作用就是告訴發送方接收到了哪條數據段。若接收方接收到了序號為n的報文段,則確認序號將是n+1,表示它已經接收了n,下一條想要接收n+1
  • 首部長度TCP報文的首部+選項的字節數;
  • ACK:只有1 bit的標志位,若為1,表示這個數據段中的確認序號是有效的,即這個數據報是對之前接收到的某個報文的確認(一個TCP報文可以同時作為確認報文和傳遞數據報文)。
  • RST:只有1 bit的標志位,若客戶端向服務器的一個端口請求建立TCP連接,但是服務器的那個端口並不允許建立連接(比如沒開啟此端口),則服務器會回送一個TCP報文,將RST位置為1,告訴客戶端不要再向這個端口發起連接;
  • SYN:只有1 bit的標志位,若為1,表示這是一條建立連接的TCP報文段;
  • FIN:只有1 bit的標志位,若為1,表示這是一條斷開連接的TCP報文段;

  對於TCP報文格式,就先介紹這么多,其余的部分雖然也很重要,但是並沒有作用於TCP連接的建立與斷開,所以就不在這里敘述了。


 2.2 TCP三次握手的過程

  TCP建立連接的過程中需要發送三次報文,所以TCP建立連接也被稱為三次握手,接下來我就來講講這三次握手的過程,假設客戶端向服務器發起TCP連接:

  • 第一步:客戶端的TCP程序首先向服務器的TCP程序發送一個TCP報文。這個報文不包含數據,且它的SYN標志位被置為1,表示這是一條建立連接的TCP報文段,因此這個報文段也被稱為SYN報文段。客戶端的TCP程序隨機選擇一個序號作為客戶端報文的初始序號(假設序號為client_isn),放入這個報文段的序號部分。這個報文段由運輸層傳遞到網絡層后,被封裝在一個IP數據報中發往服務器;
  • 第二步:包含SYN報文段的IP數據報被服務器接收,服務器的網絡層將SYN數據報抽取出來,交給運輸層,同時服務器為該TCP連接分配資源(包括發送緩存、接收緩存和變量等),並向客戶發送允許連接的TCP報文段。這條允許連接的報文段不包含數據,SYN標志位也被置為1,同時它的ACK標志位也被置為1,表示它是SYN報文段的確認報文,所以這條允許連接的報文段也被稱為SYNACK報文段。服務器隨機選擇一個序號,作為服務器報文段的初始序號(假設稱為server_isn),並將其放入SYNACK報文段的序號部分,同時確認號字段被設置為client_isn + 1SYN報文段的序號+1)。這個報文段可以解釋為服務器向客戶端說:“我收到了你的連接請求,我允許你連接,我的初始序號是server_isn”。
  • 第三步:當客戶端接收到SYNACK報文段后,它也將為TCP連接分配資源(緩存和變量),同時生成一條SYNACK報文段的確認報文,並發送給服務器。由於經過上面兩個步驟,已經算是建立了連接,所以這次的SYN標志位將被置為0,而不是1ACK標志位是1)。同時,這條報文段的序號被設置為client_isn + 1(第一條客戶報文的序號是client_isn,而這是它的下一條,所以+1),而確認序號被設置為server_isn + 1(第一條服務器報文的序號是server_isn,客戶端成功接收,所以期望服務器下一次發送server_isn + 1)。和上面兩條報文不同,第三條報文可以攜帶數據,比如HTTP的請求就是在TCP的第三次握手報文中發送到服務器的。

  經過上面這三個步驟,TCP連接就算正式建立完畢,客戶端和服務器可以相互發送數據了。下面是這個過程的圖片形式:


 2.3 為什么是三次握手而不是兩次

  首先我們要明確,兩次握手是必要的。第一次握手,客戶端將SYN報文發送到服務器,服務器接收到報文后,即可確認客戶端到服務器是可達的;而服務器向客戶端發送響應的SYNACK報文,客戶端接收到后,即可確認服務器到客戶端也是可達的。至此,連接已經算是建立,那為什么還要有第三次握手呢?

  客戶端和服務器的握手過程,不僅僅是確認互相可達的過程,更重要的是一個同步的過程,SYN就是同步(Synchronize)的縮寫。對於TCP報文段來說,序號是一個至關重要的部分,它保證了TCP傳輸數據的完整性。而我們上面也說過,TCP報文的初始序號不是從0開始的,而是一個隨機的序號,而所謂的同步,就是TCP客戶端和服務器互相同步初始序號的過程。第一次握手,客戶端發送SYN報文,將自己的初始序號發送到了服務器,服務器接收到后,向客戶端發送SYNACK報文段,告訴客戶端已經收到了它的初始序號,同時在這個報文段中帶上了自己的初始序號。這個時候,第三次握手的作用就出來了:第三次握手實際上就是客戶端在告訴服務器,自己已經收到了它的初始序號,完成了同步,可以開始相互傳輸數據了。若沒有第三次握手,服務器將無法保證客戶端接收到了自己的SYNACK報文段,若此時SYNACK報文段丟失,客戶端不知道服務器的初始序號,將無法處理之后到達客戶端的數據。

  在很多書籍和網上的博客中還流傳另外一種說法。若僅僅是兩次握手,將產生以下問題:客戶端向服務器發送SYN報文段請求建立連接,但是沒有在指定時間內收到SYNACK報文段,所以客戶端認為SYN報文段在網絡中丟失,則再次發送SYN報文段,並成功接收到了SYNACK報文段,但是客戶端在很短的時間內就斷開了TCP連接。然而,最初的SYN報文並沒有丟失,只是傳輸時延太長,過了許久才到達。等它到達服務器時,其實客戶端已經與服務器建立過TCP連接,並且已經斷開了。此時服務器接收到這條SYN報文段,以為客戶端又想建立一條新的連接,於是向客戶端回送ACK報文,並為連接分配了資源。由於沒有第三次握手,服務器將不知道這其實是上一次連接的報文,於是將它創建一個新的TCP連接並維持,直至因為太久沒有接收到數據而釋放。這種情況非常浪費資源,所以為了防止這種情況的發生,才需要客戶端的再一次確認。

  實際上,上面所述的第一點才是TCP三次握手的原因,第二個只能算是順帶的好處吧,從建立連接的報文被稱為SYN(同步)就可以看出這點。


 2.4 TCP四次揮手的過程

  說完了三次握手,下面來說說四次揮手的過程。TCP在斷開連接時,客戶端與服務器之間要交換四次報文,所以,TCP的斷開連接也叫四次揮手。

  • 第一步:客戶端進程發出斷開連接指令,這將導致客戶端的TCP程序創建一個特殊的TCP報文段,發送到服務器。這個報文段的FIN字段被置為1,表示這是一條斷開連接的報文;
  • 第二步:服務器接收到客戶端發來的斷開連接報文,向客戶端回送這個報文的確認報文(ACK字段為1),告訴服務器已經接收到FIN報文,並允許斷開連接;
  • 第三步:服務器發送完確認報文后,服務器的TCP程序創建一條自己的斷開連接報文,此報文的FIN字段被置為1,然后發往客戶端;
  • 第四步:客戶端接收到服務器發來的FIN報文段,則產生一條確認報文(ACK1),發送給服務器,告知服務器已經接收到了它的斷開報文。服務器接收到這條ACK報文段后,釋放TCP連接相關的資源(緩存和變量),而客戶端等待一段時間后(半分鍾、一分鍾或兩分鍾),也釋放處於客戶端的緩存和變量;

  以上就是四次揮手的過程,相對建立連接來說要簡單一些。以上是以客戶端請求斷開連接來舉例,但其實也可以由服務器斷開連接。


 2.5 客戶端為什么要等待一段時間再釋放資源

  看完上面四次揮手的過程,可能有的人會有疑問,四次揮手之后,連接不是已經斷開了嗎,為什么客戶端還要等待一段時間再釋放資源呢?原因有兩個:

  • 原因一:客戶端接收到服務器發送的FIN報文后(第三次揮手),會回送一條確認報文(第四次揮手),但是,客戶端並不知道這條確認報文是否可以順利到達服務器。若這條確認報文在傳送到服務器的過程中損壞、丟失或超時,將引起服務器重新發送FIN報文,客戶端接收到后,將需要再次發送一條確認報文,直到服務器正確接收。但是,客戶端發送確認報文后,立刻釋放資源,將導致無法處理重傳的FIN報文,所以客戶端需要等待一段時間,直到確認沒有出現上述情況出現再釋放資源。
  • 原因二TCP四次揮手完成后,理論上已經斷開了連接,但是這不代表之前通過這條連接發送的所有數據都處理完畢了,有些可能還在網絡中傳輸。若在四次揮手后,立即釋放客戶端的資源,然后客戶端立即以同一個源端口,向服務器的同一個目的端口再次建立一個TCP連接,這個連接和上一個的 源端口+源IP+目的端口+目的IP 都一模一樣,此時將會產生問題。若上一次連接遺留在網絡中的報文此時到達,將會被當做新連接傳輸的數據處理,於是可能會產生一些不可預估的錯誤。所以,客戶端在斷開連接后,需要等待一段時間,直到網絡中遺留的數據都死掉,才釋放資源,而在資源沒有被釋放前,是不允許建立一個 源端口+源IP+目的端口+目的IP 都一模一樣的TCP連接的(因為TCP套接字由這四部分標識)。

 2.6 斷開連接為什么需要四次揮手

  有的人可能也會想,斷開連接為什么是四次揮手,不能是兩次呢?其中一方請求斷開連接,另一方確認即可,為什么這個過程需要兩邊各發起一次?原因就是:TCP連接是全雙工的。什么是全雙工,即AB建立連接,則A可以向B發送數據,而B也可以向A發送數據。

  我們知道斷開連接的請求什么時候發起?當然就是在不再有數據需要發送時。我們依舊以客戶端向服務器斷開連接為例。假設客戶端和服務器建立了一個TCP連接,在客戶端需要向服務器發送的數據都發送完后,客戶端就可以向服務器發送一個FIN報文段,請求斷開連接;服務器接收到后,將會回送一個ACK報文,告訴客戶端,自己已經收到了它斷開連接的請求。若只有兩次揮手,這個時候連接就算是斷開了。但是這樣真的合理嗎?答案當然是否定的。

  客戶端發送完數據后,告訴服務器,我沒有數據了,可以和你斷開,但是不代表服務器沒有數據需要發送到客戶端了呀。TCP是一個全雙工的連接,代表服務器也有可能有數據需要發送到客戶端。所以,只有當兩端的數據都發送完畢,連接才能安全的斷開。因此,服務器接收到了客戶端的FIN報文段,他會等到自己所有的數據發送完,然后也向客戶端發送一個FIN報文,告訴客戶端我也沒數據了,這時候連接才能真正斷開,兩端各自釋放資源。


三、總結

  以上對TCP的三次握手和四次揮手做了一個比較全面的講解,相信看完之后可以讓人對TCP連接的建立和斷開有更深入的理解。但是,這一部分內容實際上只是TCP中比較簡單的一塊,盡管如此,還是用了這么多篇幅去講解,可想而知TCP的復雜性。我后面還將寫幾篇關於TCP其他方面的內容,希望在搜集資料和編寫的過程中,能夠加深我對它的理解。


四、參考

  1. 《計算機網絡——自頂向下方法(原書第七版)》
  2. 多篇博客


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM