可能很多人對TCP協議都不陌生,網上也有很多資料關于TCP協議的講解說明,但大多都比較片面,不能很全面的說明它的來龍去脈。小編也是整理了很久,找了很多資料,加上自己的理解寫出了這篇文章,希望能幫助大伙更好的去理解。
本篇文章內容比較多,也有比較多的生詞,大伙可能需要多閱讀幾遍外加自己的學習才能更好的去理解。
TCP/IP協議的起源
在世界上各地,各種各樣的電腦運行著各自不同的操作系統為大家服務,這些電腦在表達同一種信息的時候所使用的方法是千差萬別。就好像世界各地的口音和語言都是千差萬別的,這會讓他們無法很好的合作。計算機使用者意識到,計算機只是單兵作戰并不會發揮太大的作用。只有把它們聯合起來,電腦才會發揮出它最大的潛力。于是人們就想方設法的用電線把電腦連接到了一起。
但是簡單的連到一起是遠遠不夠的,就好像語言不同的兩個人互相見了面,完全不能交流信息。因而他們需要定義一些共通的東西來進行交流,TCP/IP就是為此而生。TCP/IP不是一個協議,而是一個協議族的統稱。里面包括了IP協議,IMCP協議,TCP協議,以及我們更加熟悉的http、ftp、pop3協議等等。電腦有了這些,就好像學會了外語一樣,就可以和其他的計算機終端做自由的交流了。
TCP/IP協議分層
TCP/IP協議族按照層次由上到下,層層包裝。
應用層:
向用戶提供一組常用的應用程序,比如電子郵件、文件傳輸訪問、遠程登錄等。遠程登錄TELNET使用TELNET協議提供在網絡其它主機上注冊的接口。TELNET會話提供了基于字符的虛擬終端。文件傳輸訪問FTP使用FTP協議來提供網絡內機器間的文件拷貝功能。
傳輸層:
提供應用程序間的通信。其功能包括:一、格式化信息流;二、提供可靠傳輸。為實現后者,傳輸層協議規定接收端必須發回確認,并且假如分組丟失,必須重新發送。
網絡層 :
負責相鄰計算機之間的通信。其功能包括三方面。
一、處理來自傳輸層的分組發送請求,收到請求后,將分組裝入IP數據報,填充報頭,選擇去往信宿機的路徑,然后將數據報發往適當的網絡接口。
二、處理輸入數據報:首先檢查其合法性,然后進行尋徑--假如該數據報已到達信宿機,則去掉報頭,將剩下部分交給適當的傳輸協議;假如該數據報尚未到達信宿,則轉發該數據報。
三、處理路徑、流控、擁塞等問題。
網絡接口層:
這是TCP/IP軟件的最低層,負責接收IP數據報并通過網絡發送之,或者從網絡上接收物理幀,抽出IP數據報,交給IP層。
TCP協議的頭部信息
對于 TCP 頭部來說,以下幾個字段是很重要的
- Sequence number,這個序號保證了 TCP 傳輸的報文都是有序的,對端可以通過序號順序的拼接報文
- Acknowledgement Number,這個序號表示數據接收端期望接收的下一個字節的編號是多少,同時也表示上一個序號的數據已經收到
- Window Size,窗口大小,表示還能接收多少字節的數據,用于流量控制
- 標識符
- URG=1:該字段為一表示本數據報的數據部分包含緊急信息,是一個高優先級數據報文,此時緊急指針有效。緊急數據一定位于當前數據包數據部分的最前面,緊急指針標明了緊急數據的尾部。
- ACK=1:該字段為一表示確認號字段有效。此外,TCP 還規定在連接建立后傳送的所有報文段都必須把 ACK 置為一。
- PSH=1:該字段為一表示接收端應該立即將數據 push 給應用層,而不是等到緩沖區滿后再提交。
- RST=1:該字段為一表示當前 TCP 連接出現嚴重問題,可能需要重新建立 TCP 連接,也可以用于拒絕非法的報文段和拒絕連接請求。
- SYN=1:當SYN=1,ACK=0時,表示當前報文段是一個連接請求報文。當SYN=1,ACK=1時,表示當前報文段是一個同意建立連接的應答報文。
- FIN=1:該字段為一表示此報文段是一個釋放連接的請求報文。
TCP協議的狀態機
TCP 的狀態機是很復雜的,并且與建立斷開連接時的握手息息相關,接下來就來詳細描述下兩種握手。
在這之前需要了解一個重要的性能指標 RTT。該指標表示發送端發送數據到接收到對端數據所需的往返時間。
建立連接的三次握手
首先假設主動發起請求的一端稱為客戶端,被動連接的一端稱為服務端。不管是客戶端還是服務端,TCP 連接建立完后都能發送和接收數據,所以 TCP 是一個全雙工的協議。
起初,兩端都為 CLOSED 狀態。在通信開始前,雙方都會創建 TCB。 服務器創建完 TCB 后便進入 LISTEN 狀態,此時開始等待客戶端發送數據。
第一次握手
客戶端向服務端發送連接請求報文段。該報文段中包含自身的數據通訊初始序號。請求發送后,客戶端便進入 SYN-SENT 狀態。
第二次握手
服務端收到連接請求報文段后,如果同意連接,則會發送一個應答,該應答中也會包含自身的數據通訊初始序號,發送完成后便進入 SYN-RECEIVED 狀態。
第三次握手
當客戶端收到連接同意的應答后,還要向服務端發送一個確認報文。客戶端發完這個報文段后便進入 ESTABLISHED 狀態,服務端收到這個應答后也進入 ESTABLISHED 狀態,此時連接建立成功。
問題:為什么建立連接需要三次,而不是兩次?
因為這是為了防止出現失效的連接請求報文段被服務端接收的情況,從而產生錯誤。
可以想象如下場景。客戶端發送了一個連接請求 A,但是因為網絡原因造成了超時,這時 TCP 會啟動超時重傳的機制再次發送一個連接請求 B。此時請求順利到達服務端,服務端應答完就建立了請求,然后接收數據后釋放了連接。
假設這時候連接請求 A 在兩端關閉后終于抵達了服務端,那么此時服務端會認為客戶端又需要建立 TCP 連接,從而應答了該請求并進入 ESTABLISHED 狀態。但是客戶端其實是 CLOSED 的狀態,那么就會導致服務端一直等待,造成資源的浪費。
斷開鏈接的四次握手
TCP 是全雙工的,在斷開連接時兩端都需要發送 FIN 和 ACK。
第一次握手
若客戶端 A 認為數據發送完成,則它需要向服務端 B 發送連接釋放請求。
第二次握手
B 收到連接釋放請求后,會告訴應用層要釋放 TCP 鏈接。然后會發送 ACK 包,并進入 CLOSE_WAIT 狀態,此時表明 A 到 B 的連接已經釋放,不再接收 A 發的數據了。但是因為 TCP 連接是雙向的,所以 B 仍舊可以發送數據給 A。
第三次握手
B 如果此時還有沒發完的數據會繼續發送,完畢后會向 A 發送連接釋放請求,然后 B 便進入 LAST-ACK 狀態。通過延遲確認的技術(通常有時間限制,否則對方會誤認為需要重傳),可以將第二次和第三次握手合并,延遲 ACK 包的發送。
第四次握手
A 收到釋放請求后,向 B 發送確認應答,此時 A 進入 TIME-WAIT 狀態。該狀態會持續 2MSL(最大段生存期,指報文段在網絡中生存的時間,超時會被拋棄) 時間,若該時間段內沒有 B 的重發請求的話,就進入 CLOSED 狀態。當 B 收到確認應答后,也便進入 CLOSED 狀態。
問題:為什么 A 要進入 TIME-WAIT 狀態,等待 2MSL 時間后才進入 CLOSED 狀態?
為了保證 B 能收到 A 的確認應答。若 A 發完確認應答后直接進入 CLOSED 狀態,如果確認應答因為網絡問題一直沒有到達,那么會造成 B 不能正常關閉。
問題:為什么建立連接是三次握手,而關閉連接卻是四次揮手呢?
這是因為服務端在LISTEN狀態下,收到建立連接請求的SYN報文后,把ACK和SYN放在一個報文里發送給客戶端。而關閉連接時,當收到對方的FIN報文時,僅僅表示對方不再發送數據了但是還能接收數據,己方也未必全部數據都發送給對方了,所以己方可以立即close,也可以發送一些數據給對方后,再發送FIN報文給對方來表示同意現在關閉連接,因此,己方ACK和FIN一般都會分開發送。
ARQ 協議
ARQ 協議也就是超時重傳機制。通過確認和超時機制保證了數據的正確送達,ARQ 協議包含停止等待 ARQ 和連續 ARQ 兩種協議。
停止等待 ARQ
正常傳輸過程
只要 A 向 B 發送一段報文,都要停止發送并啟動一個定時器,等待對端回應,在定時器時間內接收到對端應答就取消定時器并發送下一段報文。
報文丟失或出錯
在報文傳輸的過程中可能會出現丟包。這時候超過定時器設定的時間就會再次發送丟失的數據直到對端響應,所以需要每次都備份發送的數據。
即使報文正常的傳輸到對端,也可能出現在傳輸過程中報文出錯的問題。這時候對端會拋棄該報文并等待 A 端重傳。一般定時器設定的時間都會大于一個 RTT 的平均時間。
ACK 超時或丟失
對端傳輸的應答也可能出現丟失或超時的情況。那么超過定時器時間 A 端照樣會重傳報文。這時候 B 端收到相同序號的報文會丟棄該報文并重傳應答,直到 A 端發送下一個序號的報文。
在超時的情況下也可能出現應答很遲到達,這時 A 端會判斷該序號是否已經接收過,如果接收過只需要丟棄應答即可。
從上面的描述中大家肯定可以發現這肯定不是一個高效的方式。假設在良好的網絡環境中,每次發送數據都需要等待片刻肯定是不能接受的。那么既然我們不能接受這個不那么高效的協議,就來繼續學習相對高效的協議吧。
連續 ARQ
在連續 ARQ 中,發送端擁有一個發送窗口,可以在沒有收到應答的情況下持續發送窗口內的數據,這樣相比停止等待 ARQ 協議來說減少了等待時間,提高了效率。
累計確認
連續 ARQ 中,接收端會持續不斷收到報文。如果和停止等待 ARQ 中接收一個報文就發送一個應答一樣,就太浪費資源了。通過累計確認,可以在收到多個報文以后統一回復一個應答報文。報文中的 ACK 標志位可以用來告訴發送端這個序號之前的數據已經全部接收到了,下次請發送這個序號后的數據。
但是累計確認也有一個弊端。在連續接收報文時,可能會遇到接收到序號 5 的報文后,并未接收到序號 6 的報文,然而序號 7 以后的報文已經接收。遇到這種情況時,ACK 只能回復 6,這樣就會造成發送端重復發送數據的情況。
滑動窗口
在ARQ協議中,發送端有一個發送窗口。在 TCP 中,兩端其實都維護著窗口:分別為發送端窗口和接收端窗口。
發送端窗口包含已發送但未收到應答的數據和可以發送但是未發送的數據。
發送端窗口是由接收窗口剩余大小決定的。接收方會把當前接收窗口的剩余大小寫入應答報文,發送端收到應答后根據該值和當前網絡擁塞情況設置發送窗口的大小,所以發送窗口的大小是不斷變化的。
當發送端接收到應答報文后,會隨之將窗口進行滑動
滑動窗口是一個很重要的概念,它幫助 TCP 實現了流量控制的功能。接收方通過報文告知發送方還可以發送多少數據,從而保證接收方能夠來得及接收數據,防止出現接收方帶寬已滿,但是發送方還一直發送數據的情況。
Zero 窗口
在發送報文的過程中,可能會遇到對端出現零窗口的情況。在該情況下,發送端會停止發送數據,并啟動 persistent timer 。該定時器會定時發送請求給對端,讓對端告知窗口大小。在重試次數超過一定次數后,可能會中斷 TCP 鏈接。
擁塞處理
擁塞處理和流量控制不同,后者是作用于接收方,保證接收方來得及接受數據。而前者是作用于網絡,防止過多的數據擁塞網絡,避免出現網絡負載過大的情況。
擁塞處理包括了四個算法,分別為:慢開始,擁塞避免,快速重傳,快速恢復。
慢開始算法
慢開始算法,顧名思義,就是在傳輸開始時將發送窗口慢慢指數級擴大,從而避免一開始就傳輸大量數據導致網絡擁塞。想必大家都下載過資源,每當我們開始下載的時候都會發現下載速度是慢慢提升的,而不是一蹴而就直接拉滿帶寬。
慢開始算法步驟具體如下
- 連接初始設置擁塞窗口(Congestion Window) 為 1 MSS(一個分段的最大數據量)
- 每過一個 RTT 就將窗口大小乘二
- 指數級增長肯定不能沒有限制的,所以有一個閾值限制,當窗口大小大于閾值時就會啟動擁塞避免算法。
擁塞避免算法
擁塞避免算法相比簡單點,每過一個 RTT 窗口大小只加一,這樣能夠避免指數級增長導致網絡擁塞,慢慢將大小調整到最佳值。
在傳輸過程中可能定時器超時的情況,這時候 TCP 會認為網絡擁塞了,會馬上進行以下步驟:
- 將閾值設為當前擁塞窗口的一半
- 將擁塞窗口設為 1 MSS
- 啟動擁塞避免算法
快速重傳
快速重傳一般和快恢復一起出現。一旦接收端收到的報文出現失序的情況,接收端只會回復最后一個順序正確的報文序號。如果發送端收到三個重復的 ACK,無需等待定時器超時而是直接啟動快速重傳算法。具體算法分為兩種:
TCP Taho 實現如下
- 將閾值設為當前擁塞窗口的一半
- 將擁塞窗口設為 1 MSS
- 重新開始慢開始算法
TCP Reno 實現如下
- 擁塞窗口減半
- 將閾值設為當前擁塞窗口
- 進入快恢復階段(重發對端需要的包,一旦收到一個新的 ACK 答復就退出該階段),這種方式在丟失多個包的情況下就不那么好了
- 使用擁塞避免算法
TCP New Ren 改進后的快恢復
TCP New Reno 算法改進了之前 TCP Reno 算法的缺陷。在之前,快恢復中只要收到一個新的 ACK 包,就會退出快恢復。
在 TCP New Reno 中,TCP 發送方先記下三個重復 ACK 的分段的最大序號。
假如我有一個分段數據是 1 ~ 10 這十個序號的報文,其中丟失了序號為 3 和 7 的報文,那么該分段的最大序號就是 10。發送端只會收到 ACK 序號為 3 的應答。這時候重發序號為 3 的報文,接收方順利接收的話就會發送 ACK 序號為 7 的應答。這時候 TCP 知道對端是有多個包未收到,會繼續發送序號為 7 的報文,接收方順利接收并會發送 ACK 序號為 11 的應答,這時發送端認為這個分段接收端已經順利接收,接下來會退出快恢復階段。
小結
這一篇文章的內容偏多,充斥了大量的術語,需要大家反復研讀,另外對于不理解的生詞希望大伙能自行多去學習。下面總結一下這一篇文章的內容:
- 介紹TCP協議的起源、分層結構和頭部信息
- 建立連接需要三次握手,斷開連接需要四次握手
- 滑動窗口解決了數據的丟包、順序不對和流量控制問題
- 擁塞窗口實現了對流量的控制,保證在全天候環境下最優的傳遞數據