堅持苦學 TCP,終于把 TCP 協議給學明白了
TCP 是面向連接的、可靠的流協議。流就相當于不間斷的數據結構。
TCP 之所以能夠提供可靠傳輸就在于 通過 校驗和、序列號、確認應答、重發控制、連接管理以及窗口控制等機制去實現的。
下面會通過介紹 TCP 的首部格式進行展開,一一闡述 TCP 三次握手、四次揮手、滑動窗口、擁塞控制、流量控制和 UDP 協議。
TCP首部格式
- 源端口號:表示發送端的端口號,字段長 16 位。
- 目標端口號:表示接收端端口號,字段長度 16 位。
- 序列號:字段長 32 位。序列號是指發送數據的位置。每發送一次數據,就累加一次該數據字節數的大小。序列號不會從0開始,是在建立連接的時候由計算機隨機生成一個數作為初始值,通過 SYN 包傳給接收端主機。
- 確認應答號:長度是 32 位。是指下一次應該收到的數據的序列號。發送端收到這個確認應答以后可以認定這個序號之前的數據都已經被正常接收。
- 數據偏移:表示數據開始的地方離 TCP 起始處有多遠。實際上也就是表示 TCP 首部的長度。
- 保留: 該字段是為了以后擴展時使用,長度是 4 位。
- 控制位:長度為 8 位。每一位從左到右分別是 CWR、ECE、URG、ACK、PSH、RST、SYN、FIN。具體我會在講解 TCP 三次握手和四次揮手再重點說明。
- 窗口大小:該字段用于通知相同 TCP 首部的確認應答所指位置開始能接受的數據大小。TCP不允許發送超過該大小的數據。
- 校驗和:由發送端填充,接收端對TCP報文段執行CRC算法以檢驗TCP報文段在傳輸過程中是否損壞。注意,這個校驗不僅包括TCP頭部,也包括數據部分。這也是TCP可靠傳輸的一個重要保障。
- 緊急指針:該字段表示本報文中緊急數據的指針。從數據部分的首位到緊急指針所指位置為止為緊急數據。
- 選項:用于提高 TCP 的傳輸性能,是可變長的可選信息。最多包含 40 字節。
TCP 三次握手

三次握手
三次握手說明:
- 由客戶端發送建立 TCP 連接的請求報文,報文中包含 seq 序列號,是由發送端隨機生成的,并且將報文中的 SYN 字段 設置為 1,表示需要建立 TCP 連接,客戶端進入 SYN_SEND 狀態。(SYN=1.seq=x,x代表隨機生成的數值)
- 由服務器端回復客戶端發送的 TCP 連接請求報文,其中包含 seq 序列號,是由回復端隨機生成的,并且將 SYN 設置為 1,也會產生一個 ACK 字段,字段值是在客戶端發送過來的序列號 seq 基礎上加 1,以便客戶端收到信息時,知道自己的 TCP 建立請求已經得到驗證,服務端進行 SYN_SEND 狀態。(SYN =1,ACK = x+1,seq=y,y為隨機生成數值)
- 客戶端收到服務端發送的 TCP 建立驗證請求后,會使自己的序列號加 1 表示,并且再次回復 ACK 驗證請求,在服務端發過來的 seq 上加 1 進行回復,客戶端進入 ESTABLISHED 狀態,當服務端接收到請求,也進入 ESTABLISHED 狀態,TCP 握手結束。(SYN = 1,ACK = y+1,seq = x+1)
為什么三次握手?
1.檢查雙方是否都具備發送和接收數據的能力
TCP 是基于全雙工的可信傳輸協議,也就意味著數據可以同時在兩個方向上傳輸。在建立三次握手的過程中也就是在檢驗雙方發送和接收數據的能力是否具備。
第一次握手,這時候客戶端知道自己具備了發送數據的能力,但還不知道服務端是否有接收和發送數據的能力。
第二次握手,當服務端接收到報文后,回復確認報文,此時服務端知道客戶端具有發送報文的能力,并且知道自己具有接收和發送數據的能力,但還不知道客戶端是否具有接收數據的能力。
第三次握手,當客戶端收到服務端的確認報文后,知道服務端具備接收和發送數據的能力,因為服務端還不知道客戶端具備接收數據的能力,所以還需要發送一個確認報文,告知服務端自己是具有接收能力的。
2.防止重復連接
在網絡狀況比較復雜或比較差的情況下,發送方可能會連續發送多次建立連接的請求。如果 TCP 握手的次數只有兩次,那么接收方只能選擇接受請求或者拒絕連接請求,但并不清楚這次的請求是否是正常的請求。
如果是三次握手的話,客戶端在接收到服務端 seq+1 的消息之后,通過對比,就可以判斷當前連接是否是歷史連接,如果是的話就會發送終止報文給服務端終止連接。如果不是歷史連接就發送確認報文建立連接。
四次揮手
建立一個 TCP 連接需要三次握手,而終止一個 TCP 連接需要經過四次揮手,這是由于 TCP 的半關閉特性造成的,TCP 提供了連接的一端在結束它的發送后還能接收來自另一端數據的能力。

四次揮手
第一次揮手:客戶端發送一個 FIN 報文(請求連接終止:FIN = 1),報文中會指定一個序列號 seq = u。并停止再發送數據,但依然能夠接收數據,主動關閉 TCP 連接。此時客戶端處于 FIN_WAIT-1 狀態,等待服務端確認。
第二次揮手:服務端收到 FIN 之后,會發送一個 ACK 報文,且把客戶端的序號值 +1 作為 ACK 報文的序列號值,表明已經收到客戶端的報文了,此時服務端處于 CLOSE_WAIT 狀態。客戶端收到服務端的確認后,進入 FIN-WAIT-2 狀態,等待服務端發出的連接釋放報文段。
前兩次揮手 既讓服務端知道了客戶端想釋放連接,也讓客戶端知道了服務端了解了自己想要釋放連接的請求。于是,可以確認關閉客戶端到服務端方向上的連接的。
第三次揮手:如果服務端也想斷開連接,會發送 FIN 報文,且指定一個序列號。此時服務端處于 LAST_ACK 狀態,等待客戶端的確認,并停止向客戶端發送數據,但服務端仍能夠接收從客戶端傳輸過來的數據。
第四次揮手:客戶端收到 FIN 之后,一樣發送一個 ACK 報文作為應答(ack = w+1),且把服務端的序列值 +1 作為自己 ACK 報文的序號值(seq=u+1),此時客戶端處于 TIME_WAIT 狀態,并在這個狀態等待 2MSL。服務端收到從客戶端發出的 TCP 報文之后結束 LAST-ACK 階段,進入 CLOSED 階段。
客戶端等待完 2MSL 之后,結束 TIME-WAIT 階段,進入 CLOSED 階段,由此完成四次揮手。
為什么客戶端在 TIME-WAIT 階段要等 2MSL?
為的是確認服務端是否收到客戶端發出的 ACK 確認報文。
當客戶端發出最后的 ACK 確認報文后,并不能確定服務端能夠接收到,所以在發完之后,等待 2MSL,服務端在 1MSL 內沒有收到客戶端發出的 ACK 確認報文,就會再次向客戶端發出 FIN 報文。
窗口滑動
TCP 以 1 個段為單位,每發一個段就要進行一次確認應答的處理。這種傳輸方式有個缺點,包往返時間越長通信性能越低。

為了解決這個問題,引入了窗口這個概念。確認應答不再是以一個分段,而是以更大的單位進行確認,發送端發了一個段后不需要一直等待確認應答,而是能夠繼續發送。

窗口大小就是指無需等待確認應答而可以繼續發送數據的最大值,窗口大小分為四個段。

如上圖所示,白色部分就是窗口,窗口內的數據即便沒有收到確認應答也可以發送出去。
滑動窗口以外的部分包括尚未發送的數據和已經確認對端收到的數據。收到確認應答后,會將窗口滑動到確認應答中的
序列號的位置。這樣就可以順序的將多個段同時發送提高通信性能。這種機制就是窗口滑動機制。
窗口滑動機制下重發處理
在使用窗口控制時,可能會遇到段丟失。在沒有使用窗口控制,沒有收到確認應答的數據會被重發,使用了窗口滑動,默寫應答即使丟失也不需要進行重發。

結合下圖所示,當某段報文丟失后,發送端會一直收到序號 1001 的確認應答,這個確認應答就像在提醒發送端"我想接收的是從 1001 開始的數據"。
出現報文丟失的情況下,同一個序號的確認應答會重復發送。當發送端連續三次收到同一個應答,就會將所對應的數據進行重發,這種重發機制叫做高速重發機制。

流量控制
TCP 提供了一種機制可以讓發送端根據接收端的實際接收能力控制發送的數據量。
接收端主機向發送端主機通知自己可以接收的數據大小,發送端就會發送不超過這個限度的數據。其實這個大小就是窗口大小,窗口大小的值是由接收端決定的。
擁塞控制
因為計算機是個共享的環境,也有可能會因為其他主機之間的通信造成網絡擁堵。在網絡出現擁堵時,突然發送一個較大量的數據可能會導致網絡癱瘓,如果在通信一開始就發送大量數據,也會引發一些問題。
TCP 為了防止這個問題,會利用一個慢啟動的算法,對發送數據量進行控制。

為了調節發送的量,定義了一個叫做擁塞窗口的概念。
在慢啟動的時候,將擁塞窗口大小設置為 1 個數據段(1 MSS)大小。之后每收到一個確認應答,就將窗口值加 1。
在發送數據包的時候,將擁塞窗口大小與接收端主機通知的窗口大小做對比,按它們中較小的值,發送比其還要小的數據量。
TCP 與 UDP 區分
UDP是面向無連接的協議, 是一個不具有可靠性的數據報協議。不確保消息一定會到達。
UDP 主要用于對高速傳輸和實時性有較高要求的通信或廣播通信。例如打電話,如果使用 TCP ,數據在傳輸過程如果有丟失就會被重發,但這樣就無法流暢的傳輸人的聲音,會造成聲音大幅度延遲。
UDP 適用范圍:
- 包總量較少的通信
- 視頻、音頻等多媒體通信
- 限定于 LAN 等特定網絡中的應用通信
- 廣播通信



























