1. TCP概念相關
[!NOTE]
TCP(Transmission Control Protocol),又叫傳輸控制協議,
TCP協議是面向連接的,可靠的,基于位元組流的傳輸協議,在基于 TCP 進行通信時,通信雙方需要先建立一個 TCP 連接,建立連接需要經過三次握手,斷開連接的時候需要經過四次揮手,
1.1 TCP頭部

對于 TCP 頭部來說,以下幾個欄位是很重要的:
-
序列號(Sequence number),這個序號保證了 TCP 傳輸的報文都是有序的,對端可以通過序號順序的拼接報文 -
確認號(Acknowledgement Number),這個序號表示資料接收端期望接收的下一個位元組的編號是多少,同時也表示上一個序號的資料已經收到 -
視窗大小(Window Size),表示還能接收多少位元組的資料,用于流量控制 -
識別符號- ACK=1 :該欄位為一表示確認號欄位有效,此外,TCP 還規定在連接建立后傳送的所有報文段都必須把 ACK 置為一,
- SYN=1:當SYN=1,ACK=0時,表示當前報文段是一個連接請求報文,當SYN=1,ACK=1時,表示當前報文段是一個同意建立連接的應答報文,
- FIN=1:該欄位為一表示此報文段是一個釋放連接的請求報文,
- URG=1 : 該欄位為一表示本資料報的資料部分包含緊急資訊,是一個高優先級資料報文,此時緊急指標有效,緊急資料一定位于當前資料包資料部分的最前面,緊急指標標明了緊急資料的尾部,
- PSH=1 :該欄位為一表示接收端應該立即將資料 push 給應用層,而不是等到緩沖區滿后再提交,
- RST=1:該欄位為一表示當前 TCP 連接出現嚴重問題,可能需要重新建立 TCP 連接,也可以用于拒絕非法的報文段和拒絕連接請求,
1.2 三次握手

簡單的說:
-
第一次握手
- SYN = 1, seq(client) = x
- 客戶端向服務端發送連接請求報文段,該報文段中包含自身的資料通訊初始序號,請求發送后,客戶端便進入 SYN-SENT 狀態,
-
第二次握手
- SYN = 1,ACK = 1,確認序號 = x+1, seq(server) = y
- 服務端收到連接請求報文段后,如果同意連接,則會發送一個應答,該應答中也會包含自身的資料通訊初始序號,發送完成后便進入 SYN-RECEIVED 狀態
-
第三次握手
- ACK = 1,確認序號 = y+1, seq(client) = x + 1
- 客戶端收到連接同意的應答后,還要向服務端發送一個確認報文,客戶端發完這個報文段后便進入ESTABLISHED 狀態,服務端收到這個應答后也進入 ESTABLISHED 狀態,此時連接建立成功,
1.3 為什么不用兩次握手?
[!NOTE]
主要是為了防止已經失效的連接請求報文突然又傳送到了服務器,從而產生錯誤,
假設有這樣一種場景, 客戶端發送的第一個請求連接并且沒有丟失,但是被滯留的時間太長,由于TCP的客戶端遲遲沒有收到確認報文,以為服務器沒有收到,此時重新向服務器發送報文,
而現在第一個請求到達服務端,這個請求已經報廢了,但是又會建立連接,
如果采用的是三次握手,就算是那一次失效的報文傳送過來了,服務端接受到了那條失效報文并且回復了確認報文,但是客戶端不會再次發出確認,由于服務器收不到確認,就知道客戶端并沒有請求連接,
1.3 為什么建立連接是三次握手,四次不可以嗎
第一次握手:
Client什么都不能確認
Server確認了對方發送正常
第二次握手:
Client確認:自己發送/接收正常,對方發送/接收正常
Server確認:自己接收正常 ,對方發送正常
第三次握手:
Client確認:自己發送/接收正常, 對方發送/接收正常
Server確認:自己發送/接收正常,對方發送/接收正常
所以通過三次握手確認雙方收發功能都正常,四次也可以但是顯得比較多余
1.4 四次揮手

TCP 是全雙工的,在斷開連接時兩端都需要發送 FIN 和 ACK,
-
第一次揮手
- 若客戶端 A 認為資料發送完成,則它需要向服務端 B 發送連接釋放請求,
-
第二次揮手
- B 收到連接釋放請求后,會告訴應用層要釋放 TCP 鏈接,然后會發送 ACK 包,并進入 CLOSE_WAIT 狀態,表示 A 到 B 的連接已經釋放,不接收 A 發的資料了,但是因為 TCP 連接時雙向的,所以 B 仍舊可以發送資料給 A,
-
第三次揮手
- B 如果此時還有沒發完的資料會繼續發送,完畢后會向 A 發送連接釋放請求,然后 B 便進入LAST-ACK狀態,
- PS:通過延遲確認的技術(通常有時間限制,否則對方會誤認為需要重傳),可以將第二次和第三次握手合并,延遲 ACK 包的發送,
-
第四次揮手
- A 收到釋放請求后,向 B 發送確認應答,此時 A 進入 TIME-WAIT 狀態,該狀態會持續 2MSL(最大段生存期,指報文段在網路中生存的時間,超時會被拋棄) 時間,若該時間段內沒有 B 的重發請求的話,就進入 CLOSED 狀態,當 B 收到確認應答后,也便進入 CLOSED 狀態,
1.5 為什么 A 要進入 TIME-WAIT 狀態,等待 2MSL 時間后才進入 CLOSED 狀態?
為了保證 B 能收到 A 的確認應答,若 A 發完確認應答后直接進入 CLOSED 狀態,如果確認應答因為網路問題一直沒有到達,那么會造成 B 不能正常關閉,
如果A發送完ACK應答之后直接進入CLOSED狀態的話,如果因為網路延遲問題這個應答丟失或在2MSL內還沒有到達B的話,那么B等待超時之后就會重新發送一個FIN包,但是此時A已經關閉了,永遠得不到A的回應,從而導致B永遠不能正常關閉
1.6 為什么需要TIME_WAIT狀態
1.6.1 為實作TCP這種全雙工連接的可靠釋放
這樣可讓TCP再次發送最后的ACK以防這個ACK丟失(另一端超時并重發最后的FIN)這種2MSL等待的另一個結果是這個TCP連接在2MSL等待期間,定義這個連接的插口(客戶的IP地址和埠號,服務器的IP地址和埠號)不能再被使用,這個連接只能在2MSL結束后才能再被使用,
1.6.2 為使舊的資料包在網路因過期而消失
每個具體TCP實作必須選擇一個報文段最大生存時間MSL,它是任何報文段被丟棄前在網路內的最長時間,
1.7 為什么建立連接是三次握手,關閉連接確是四次揮手呢?
-
建立連接的時候, 服務器在LISTEN狀態下,收到建立連接請求的SYN報文后,把ACK和SYN放在一個報文里發送給客戶端,
-
而關閉連接時,服務器收到對方的FIN報文時,僅僅表示對方不再發送資料了但是還能接收資料,而自己也未必全部資料都發送給對方了
2. ARQ (超時重傳)協議
[!NOTE]
通過確認和超時機制保證了資料的正確送達,ARQ 協議包含停止等待 ARQ和連續 ARQ
2.1 停止等待 ARQ
正常傳輸程序
只要 A 向 B 發送一段報文,都要停止發送并啟動一個定時器,等待對端回應,在定時器時間內接收到對端應答就取消定時器并發送下一段報文,
當報文丟失或出錯:
報文傳輸的程序中丟包: 這時候超過定時器設定的時間就會再次發送丟包的資料直到對端回應,所以需要每次都備份發送的資料,
傳輸程序中報文出錯: 對端會拋棄該報文并等待 A 端重傳,
PS:一般定時器設定的時間都會大于一個 RTT 的平均時間,
ACK 超時或丟失:
對端傳輸的應答也可能出現丟失或超時的情況,那么超過定時器時間 A 端照樣會重傳報文,這時候 B 端收到相同序號的報文會丟棄該報文并重傳應答,直到 A 端發送下一個序號的報文,
這個協議的缺點就是傳輸效率低,在良好的網路環境下每次發送報文都得等待對端的 ACK ,
2.2 連續 ARQ
在連續 ARQ 中,發送端擁有一個發送視窗,可以在沒有收到應答的情況下持續發送視窗內的資料,這樣相比停止等待 ARQ 協議來說減少了等待時間,提高了效率,
2.2.1 累計確認
連續 ARQ 中,接收端會持續不斷收到報文,如果和停止等待 ARQ 中接收一個報文就發送一個應答一樣,就太浪費資源了,通過累計確認,可以在收到多個報文以后統一回復一個應答報文,報文中的 ACK 可以用來告訴發送端這個序號之前的資料已經全部接收到了,下次請發送這個序號 + 1的資料,
但是累計確認也有一個弊端,在連續接收報文時,可能會遇到接收到序號 5 的報文后,并未接到序號 6 的報文,然而序號 7 以后的報文已經接收,遇到這種情況時,ACK 只能回復 6,這樣會造成發送端重復發送資料,這種情況下可以通過 Sack 來解決,
2.2.2 滑動視窗
上面講到了發送視窗,在 TCP 中,兩端都維護著視窗:分別為發送端視窗和接收端視窗,
發送端視窗包含已發送但未收到應答的資料和可以發送但是未發送的資料,

發送端視窗是由接收視窗剩余大小決定的,接收方會把當前接收視窗的剩余大小寫入應答報文,發送端收到應答后根據該值和當前網路擁塞情況設定發送視窗的大小,所以發送視窗的大小是不斷變化的,
當發送端接收到應答報文后,會隨之將視窗進行滑動
滑動視窗實作了流量控制,接收方通過報文告知發送方還可以發送多少資料,從而保證接收方能夠來得及接收資料,

Zero 視窗
在發送報文的程序中,可能會遇到對端出現零視窗的情況,在該情況下,發送端會停止發送資料,并啟動 persistent timer ,該定時器會定時發送請求給對端,讓對端告知視窗大小,在重試次數超過一定次數后,可能會中斷 TCP 鏈接,
3. 擁塞處理
[!NOTE]
擁塞處理和流量控制不同,后者是作用于接收方,保證接收方來得及接受資料,而前者是作用于網路,防止過多的資料擁塞網路,避免出現網路負載過大的情況,
擁塞處理包括了四個演算法,分別為:慢開始,擁塞避免,快速重傳,快速恢復,

3.1 慢開始演算法
[!NOTE]
慢開始演算法,顧名思義,就是在傳輸開始時將發送視窗從1開始指數級擴大,從而避免一開始就傳輸大量資料導致網路擁塞,
慢開始演算法步驟具體如下
- 連接初始設定擁塞視窗(Congestion Window) 為 1 MSS(一個分段的最大資料量)
- 每過一個 RTT (往返時延) 就將視窗大小乘二
- 指數級增長肯定不能沒有限制的,所以有一個閾值限制,當視窗大小大于閾值時就會啟動擁塞避免演算法
3.2 擁塞避免演算法
[!NOTE]
擁塞避免演算法相比簡單點,每過一個 RTT 視窗大小只加一,這樣能夠避免指數級增長導致網路擁塞,慢慢將大小調整到最佳值,

在傳輸程序中可能定時器超時的情況,這時候 TCP 會認為網路擁塞了,會馬上進行以下步驟:
- 將閾值設為當前擁塞視窗的一半
- 將擁塞視窗設為 1 MSS
- 啟動擁塞避免演算法
3.3 快速重傳
快速重傳一般和快恢復一起出現,一旦接收端收到的報文出現失序的情況,接收端只會回復最后一個順序正確的報文序號(沒有 Sack 的情況下),如果收到三個重復的 ACK,無需等待定時器超時再重發而是啟動快速重傳,具體演算法分為兩種:
4. TCP 小結
4.1 為什么TCP這么復雜?
[!NOTE]
因為既要保證可靠性, 同時又要盡可能提高性能
4.1.1 保證可靠性的機制
- 校驗和
- 序列號(按序到達)
- 確認應答
- 超時重傳
- 連接管理
- 流量控制
- 擁塞控制
4.1.2 提高性能的機制
- 滑動視窗
- 快速重傳
- 延遲應答
- 捎帶應答
4.2 定時器
- 超時重傳定時器
- 保活定時器
- TIME_WAIT定時器
4.3 基于 TCP 的應用層協議
- HTTP
- HTTPS
- SSH
- Telnet
- FTP
- SMTP
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/5219.html
標籤:其他
