TCP 協議詳解

TCP 協議詳解

在筆者以前的工程中,用過 socket 套接字實現過多進程通信的程序,也用過 Node.js + socket.io + express 構建過 B/S 軟件架構,但對最基礎的 TCP 協議其實並沒有透徹瞭解。適逢筆者最近找工作,爲防面試時被面試官問到相關問題慘遭打臉,筆者決定總結一下 TCP 協議的相關知識點。

參考網址:
《TCP 協議詳解》
《簡析TCP的三次握手與四次分手》
《TCP協議中的三次握手和四次揮手(圖解)》
《TCP通信的三次握手和四次撒手的詳細流程(頓悟) 》
《TCP建立連接的三次握手(例題)》

一、TCP 協議功能簡述

在世界上各地,各種各樣的電腦運行着各自不同的操作系統爲大家服務,這些電腦在表達同一種信息的時候所使用的方法是千差萬別。就好像聖經中上帝打亂了各地人的口音,讓他們無法合作一樣。計算機使用者意識到,計算機只是單兵作戰並不會發揮太大的作用。只有把它們聯合起來,電腦纔會發揮出它最大的潛力。於是人們就想方設法的用電線把電腦連接到了一起。但是簡單的連到一起是遠遠不夠的,就好像語言不同的兩個人互相見了面,完全不能交流信息。因而他們需要定義一些共通的東西來進行交流,TCP / IP 就是爲此而生。
TCP / IP 不是一個協議,而是一個協議族的統稱。裏面包括了 IP 協議,IMCP 協議,TCP 協議,以及我們更加熟悉的 http、ftp、pop3 協議等等。電腦有了這些,就好像學會了外語一樣,就可以和其他的計算機終端做自由的交流了。


# 二、TCP / IP 協議分層 網絡結構有兩種分層模式:OSI 參考模型與 TCP / IP 參考模型,兩者雖然不同,但卻有很多共通之處,如下所示:

TCP / IP 協議按照層次由上到下,層層包裝。TCP / IP 模型各層之間的基本作用如下:

  • 應用層:向用戶提供一組常用的應用程序,如電子郵件(簡單郵件傳輸協議,SMTP),文件傳輸訪問(文件傳輸協議,FTP),遠程登錄(TELNET)等。
    • 遠程登錄 TELNET:使用 TELNET 協議,提供在網絡其它主機上註冊的接口,TELNET 會話提供了基於字符的虛擬終端;
    • 文件傳輸訪問 FTP:使用 FTP 協議來提供網絡內機器間的文件拷貝功能;
  • 傳輸層:即圖中的運輸層,負責提供應用程序間的通信。其功能包括:
    • 格式化信息流;
    • 提供可靠傳輸。爲實現後者,傳輸層協議規定接收端必須發回確認,並且假如分組丟失,必須重新發送。
  • 網絡層:負責相鄰計算機之間的通信。功能主要包括三方面:
    • 處理來自傳輸層的分組發送請求:收到請求之後,將分組裝入 IP 數據報,填充報頭,選擇去往信宿機的路徑,然後將數據報發往適當的網絡接口;
    • 處理輸入數據報:首先檢查其合法性,然後進行尋址:如果該數據包已經到達信宿機,則去掉報頭,將剩下一部分交給適當的傳輸協議;如果該數據包尚未到達信宿機,則轉發該數據報;
    • 處理路徑、流控、擁塞等問題;
  • 網絡接口層:這是 TCP / IP 的最底層,負責接收 IP 數據報並通過網絡發送數據報,或者從網絡上接收物理幀,抽出 IP 數據報,交給 IP 層;

三、TCP 的可靠連接

TCP 用於應用程序之間的可靠通信。
當應用程序希望通過 TCP 與另一個應用程序通信時,它會發送一個通信請求,這個請求必須被送到一個一個確切的地址。在雙方“握手”之後,TCP 將在兩個應用程序之間建立一個全雙工 (Full-Duplex) 的通信,這個全雙工通信將佔用兩個計算機之間的通信線路,直到它被一方(或雙方)關閉爲止。
:UDP 和 TCP 很相似,當時更簡單,同時可靠性低於 TCP。


# 四、TCP 報文格式 TCP 是一個協議,那這個協議是如何定義的,它的數據格式是什麼樣子的呢?要進行更深層次的剖析,就需要了解,甚至是熟記 TCP 協議中每個字段的含義。下圖就是 TCP 協議的報文格式,由於報文格式是理解其它內容的基礎,十分重要,所以將對每個字段的信息詳細說明:
  • Source Port, Destination Port(源端口號,目的端口號):分別佔用 16 位,用於區別主機中的不同進程;由於 IP 地址用來區分不同主機,所以源端口號、目的端口號與 IP 首部中的源 IP 地址和目的 IP 地址,技能確定唯一的一個 TCP 連接;
  • Sequence Number(發送序號):32 位數據,用來標識從 TCP 發送端向 TCP 接收端發送的數據字節流,它表示在這個報文段中的第一個數據字節在數據流中的序號,主要用來解決網絡報亂序問題;
  • Acknowledgment Number(確認序號):佔用 32 位,由接收端的計算機使用,將分段的報文重組成最初形式;如果設置了控制位 ACK = 1,則這個值表示下一個準備接受的包的序列碼;
  • Offset(數據偏移量):佔用 4 位,給出首部中 32bit 字的數目,需要這個值是因爲任選字段的長度是可變的(如果沒有任選字段,正常的長度是 20 字節);
  • Reserved(保留位):佔用 6 位,且必須是 0,爲了將來定義新的用途而保留;
  • TCP Flags(TCP 標誌位):用於標誌 TCP 的某些狀態,它們中的多個可同時被設置爲 1,主要用於操控 TCP 的狀態機,6 個標誌位依次爲 URG, ACK, PSH, RST, SYN, FIN。每個標誌位的意義如下:
    • URG:緊急標誌 (Urgent),該標誌表示 TCP 包的緊急指針域有效(後面將會說到緊急指針域的內容),用來保證 TCP 連接不被中斷,並督促中間層設備要儘快處理這些數據;
    • ACK:確認標誌 (Acknowledge),該標誌表示應答域有效,就是說前面提到的 TCP 應答信號會包含在 TCP 數據包中;ACK 可以由兩個取值( 0/1 ):應答域有效爲1,反之爲0;
    • PSH:推標誌 (Push),表示 Push 操作,即在數據報到達接收端以後,立即傳送給應用程序,而不是在緩衝區中排隊;
    • RST:復位標誌 (Reset),用來複位那些產生錯誤的連接,也被用來拒絕錯誤和非法的數據報;
    • SYN:同步標誌 (Synchronize),用來建立連接。該標誌經常與 ACK 標誌搭配使用:
      • 連接請求時,SYN = 1, ACK = 0;
      • 連接被響應時,SYN = 1, ACK = 1;
      • SYN 的數據報經常被用來進行端口掃描,掃描這發送一個只有 SYN 的數據包,此時若對方主機相應了一個數據包回來,就表明這臺主機存在該端口;
      • 這種掃描方式只是進行 TCP 三次握手的第一次握手,因此這種掃描的成功表示被掃描的機器並不安全,一臺安全的主機將會強制要求一個連接嚴格的進行 TCP 三次握手;
    • FIN:結束標誌 (Finish),表示發送端已經達到數據末尾,也就是說雙方的數據傳送完成,沒有數據可以傳送了,發送FIN標誌位的TCP數據包後,連接將被斷開。這個標誌的數據包也經常被用於進行端口掃描;
  • Window(窗口大小):用來進行流量控制(問題比較複雜,本博文中並不總結);

五、TCP 的三次握手

## 1. 三次握手詳解 TCP 是面向連接的,無論哪一方向另一方發送數據之前,都必須先在雙方之間建立一條連接。在 TCP / IP 協議中,TCP 協議提供可靠的連接服務,連接是通過三次握手進行初始化的。 三次握手的目的是同步連接雙方的序列號和確認號並交換 TCP 窗口大小信息。這就是面試中經常會被問到的 TCP 三次握手。解釋如下圖:
  • 第一次握手:建立連接。首先客戶端發送連接請求報文段,將同步位 SYN 置爲 1,發送序號 (Sequence Number) 置爲 x;然後客戶端進入 SYN_SEND 狀態,等待服務器確認;
  • 第二次握手:服務器收到 SYN 報文段。服務器收到了客戶端發送的 SYN 報文段,對該 SYN 報文段進行確認,設置確認標誌 Acknowlegde Number 爲 x + 1(即發送序號 Sequence Number + 1);同時服務器自己還要發送 SYN 請求信息,將 SYN 置爲 1,發送序號 Sequence Number 爲 y;服務器端將上述所有信息放到一個報文段(即 SYN + ACK 報文段)中,一併發給客戶端;此時服務器進入 SYN_RECV 狀態;
  • 第三次握手:客戶端收到服務器的 SYN + ACK 報文段,然後將確認序號 Acknowledgment Number 設置爲 y+1,向服務器發送 ACK 報文段,這個報文段發送完畢後,客戶端與服務器端都進入了 ESTABLISHED 狀態,此時便完成了 TCP 三次握手;

完成了三次握手,客戶端和服務器端就可以開始傳送數據。以上就是TCP三次握手的總體介紹。

2. 爲什麼要進行三次握手?

既然總結了TCP的三次握手,那爲什麼非要三次呢?怎麼覺得兩次就可以完成了。那TCP爲什麼非要進行三次連接呢?在謝希仁的《計算機網絡》中是這樣說的:

爲了防止已失效的連接請求報文段突然又傳送到了服務端,因而產生錯誤。

同時,書中舉了一個例子如下:

“已失效的連接請求報文段”的產生在這樣一種情況下:客戶端發出的第一個連接請求報文段並沒有丟失,而是在某個網絡結點長時間的滯留了,以致延誤到連接釋放以後的某個時間纔到達服務器。本來這是一個早已失效的報文段,但服務器收到此失效的連接請求報文段後,就誤認爲是客戶端再次發出的一個新的連接請求。於是就向客戶端發出確認報文段,同意建立連接。假設不採用“三次握手”,那麼只要服務器發出確認,新的連接就建立了。由於現在客戶端並沒有發出建立連接的請求,因此不會理睬服務器的確認,也不會向服務器發送數據。但服務器卻以爲新的運輸連接已經建立,並一直等待客戶端發來數據。這樣,服務器的很多資源就白白浪費掉了。採用“三次握手”的辦法可以防止上述現象發生。例如剛纔那種情況,客戶端不會向服務器的確認發出確認。服務器由於收不到確認,就知道客戶端並沒有要求建立連接。”

這樣講就很明白了,防止了服務器端的一直等待而浪費資源。


# 六、TCP 的三次握手實例講解 例1:實例如下: > IP 192.168.1.116.3337 –> 192.168.1.123.7788: S 3626544836:3626544836 > IP 192.168.1.123.7788 –> 192.168.1.116.3337: S 1739326486:1739326486 ack 3626544837 > IP 192.168.1.116.3337 –> 192.168.1.123.7788: ack 1739326487,ack 1 - 第一次握手:192.168.1.116 發送位碼 SYN = 1,隨機產生髮送序列號Sequence Number = 3626544836 的數據包,併發送到 IP 爲 192.168.1.123 的地址,192.168.1.123 由 SYN = 1 知道 192.168.1.116 要求建立聯機; - 第二次握手:192.168.1.123 收到請求後要確認聯機信息,向 192.168.1.116 確認序列號 (Acknowledge Number) ACK = 3626544837, SYN = 1, ACK = 1,隨機產生髮送序列號seq = 1739326486 的包; - 第三次握手:192.168.1.116 收到後檢查第二次握手中收到的確認序列號 (ACK = 3626544837) 是否正確,即是否等於第一次發送的發送序列號 Sequence Number + 1 (3626544836 + 1),以及位碼 ACK 是否爲 1,若正確,192.168.1.116 會再發送確認序列號 Acknowledge Number = 1739326487, ACK = 1,192.168.1.123 收到後確認 seq = seq + 1, ack = 1,則連接建立成功。 用網絡抓包分析工具 WireShark 可以分析上述過程如下面兩圖所示:

第一次握手的標誌位如下圖所示:

我們看到標誌位中只有一個同步位爲 1,也就是說此時在做請求 (SYN);

第二次握手的標誌位如下圖所示:

我們可以看到,標誌位裏只有確認位和同步位,也就是正在做應答(SYN + ACK);

第三次握手的標誌位如下圖所示:

我們可以看到,標誌位裏只有一個確認位,也就是正在做再次確認 (ACK);

故可以得知:一次完整的三次握手,就是請求 -> 應答 -> 再次確認;

例2,有題如下:

TCP建立連接的過程採用三次握手,已知第三次握手報文的發送序列號爲 1000,確認序列號爲 2000,請問第二次握手報文的發送序列號和確認序列號分別爲
A. 1999,999
B. 1999,1000
C. 999,2000
D. 999,1999

答案應該選 B:發送序列 (Sequence Number)是自己發送報文的序列號,當前發送序列號是上一次發送序列號 +1,確認序列號 (Acknowledge Number) 是從對方接收到的發送序列號 +1。第三次握手發送的序列號(即 seq number = x+1)是 1000,那說明第一次握手發送的序列號(即seq number = x)是 999。
注意:這裏是握手,因此,第二次握手的確認序列號是 1000,即第二次握手的確認序列號是第一次握手時從對方接收到的發送序列號(即上文中推出來的999)+1。第三次握手發送的確認號是 2000,說明第二次握手的發送序列號是 1999。所以選 B。

七、TCP 的四次分手

1. 四次分手詳解

在客戶端和服務器通過三次握手建立了TCP連接以後,當數據傳送完畢,肯定總要斷開 TCP 連接。那對於 TCP 的斷開連接,這裏就有了對應的“四次分手”。

  • 第一次分手:主機 1(可以是客戶端,也可以是服務器端),設置發送序列號 (Sequence Number) 和確認序列號 (Acknowledgment Number),向主機 2 發送一個 FIN 報文段;這時候,主機 1 進入 FIN_WAIT_1 狀態;這表示主機 1 沒有數據要發送給主機 2 了;
  • 第二次分手:主機 2(收到了主機 1 發送的 FIN 報文段,向主機 1 回一個 ACK 報文段,此時確認序列號 (Acknowledge Number) 設置爲第一次分手階段中的發送序列號 (Sequence Number) 的值 + 1;主機 1 進入 FIN_WAIT_2 狀態;主機 2 告訴主機 1:“我同意你的關閉請求”;
  • 第三次分手:主機 2 向主機 1 發送 FIN 報文段,請求關閉連接,同時主機 2 進入 LAST_ACK 狀態;
  • 第四次分手:主機 1 收到主機 2 發送的 FIN 報文段,然後主機 1 進入 TIME_WAIT 狀態;主機 2 收到主機 1 的 ACK 報文段之後,就關閉連接;此時主機 1 等待 2MSL(最大報文段生存時間)後依然沒有收到回覆,則證明服務器端已經正常關閉,這時候主機 1 也可以關閉連接了。

這次 TCP 的四次分手就這麼完成了。

2. 爲什麼要四次分手?

四次分手是爲何呢?如果要正確的理解四次分手的原理,就需要了解四次分手過程中的狀態變化。

  • FIN_WAIT_1(主動方):該狀態需要好好解釋一下。其實 FIN_WAIT_1 和 FIN_WAIT_2 狀態的真正含義,都是表示等待對方的 FIN 報文,而這兩種狀態的區別是:FIN_WAIT_1 狀態實際上是 socket 在 ESTABLISHED 狀態時,它想主動關閉連接,向對方發送了 FIN 報文,此時該 socket 進入到了 FIN_WAIT_1 狀態;而當對方迴應 ACK 報文後,則進入到 FIN_WAIT_2 狀態,當然在實際的正常情況下,無論對方何種情況下,都應該馬上回應 ACK 報文,所以 FIN_WAIT_1 狀態一般都比較難見到,而 FIN_WAIT_2 狀態還可以時常用 netstat 看到;
  • FIN_WAIT_2(主動方):上面已經詳細解釋了這種狀態,實際上 FIN_WAIT_2 狀態下的 socket 表示半連接,即由一方要求關閉連接,但同時還告訴對方,我暫時還有一點數據需要傳送給你 (ACK),稍後再關閉連接;
  • CLOSE_WAIT(被動方):這種狀態含義其實是表示等待關閉。當對方關閉一個 socket 後,對方會發送一個 socket 給自己,此時系統必然會迴應一個 ACK 報文給對方,此時進入到 CLOSE_WAIT 狀態。接下來,實際上你真正需要考慮的事情是查看你是否還有數據發送給對方。如果沒有的話,就可以關閉這個 socket,發送 FIN 報文給對方,即關閉了連接。所以在 CLOSE_WAIT 狀態下,需要完成的事情是等待你去關閉連接;
  • LAST_ACK(被動方):被動關閉一方發送 FIN 報文後,最後等待對方的 ACK 報文;當收到 ACK 報文後,也就可以進入到 CLOSED 可用狀態了;
  • TIME_WAIT(主動方):表示收到了對方的 FIN 報文,併發送出了 ACK 報文,就等 2MSL 後就可回到 CLOSED 可用狀態了。如果 FIN_WAIT_1 狀態下,收到了對方同時帶 FIN 標誌和 ACK 標誌的報文時,就可以直接進入到 TIME_WAIT 狀態,而無須經過 FIN_WAIT_2 狀態;
  • CLOSED:表示連接中斷;

八、後記

基本上介紹到這裏就結束了,但 TCP 協議還是比較複雜的,需要好好理解。
希望這次總結可以讓筆者對 TCP 協議更加深入瞭解,而且能夠在以後的某不知何時何地的面試中過關斬將吧~

發佈了60 篇原創文章 · 獲贊 259 · 訪問量 47萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章