《計算機網路自頂向下方法》第三章 傳輸層
計算機網路——傳輸層
傳輸層位於應用層和網路層之間,是分層的網路體系結構中重要的部分,該層為執行在不同主機上的應用程序提供直接的通訊服務起著至關重要的作用。在這裡我們將討論兩個大的問題:將網路層在不同端系統之間的通訊服務擴充到執行在兩個端系統上不同的應用層程序之間的通訊服務(如何實現進城之間通訊的可靠傳輸)和控制傳輸層實體的傳輸速度以避免網路擁塞或者從網路擁塞中恢復過來,這裡需要考慮的有擁塞的後果和原因以及常見的擁塞控制手段,特別的,我們將瞭解TCP協議中的擁塞控制。傳輸層概述
傳輸層協議為執行在不同端系統上的應用程序之間提供邏輯通訊功能;應用層程序使用傳輸層提供的邏輯通訊功能而無需考慮實現通訊的物理基礎設施的細節;
值得注意的是,傳輸層協議是在端系統中實現的而不是在路由器中實現的。傳輸層接收來自應用層的報文並通過新增傳輸層首部以生成傳輸層報文段。在生成過程中可能會對來自應用層的報文加以分割;然後在傳送端系統中,運輸層會將這些報文段交給網路層;網路層將其封裝成網路層分組,也被稱為資料報,然後向目的地傳送。路由器不會檢查封裝在資料報中的傳輸層報文段的欄位;在接收端,網路層從資料報中抽取傳輸層報文段,並將其交給傳輸層,傳輸層接收到報文段後,使該報文段中的資料被接收程序所使用。
網路應用可以使用多種傳輸層協議,因特網有兩種傳輸層協議,即TCP和UDP,不同的傳輸層協議提供不同的運輸層服務;
傳輸層和網路層的關係
值得注意的是,網路層提供主機之間的邏輯通訊而傳輸層為執行在不同主機上的應用程序提供邏輯通訊;運輸層協議只工作在端系統中。在端系統中,傳輸層協議將來自應用程序的報文移動到網路邊緣即網路層,反過來也從網路層接收這些報文段;傳輸層對報文段如何在網路核心傳輸並不做干涉;事實上中間路由器既不處理也不識別傳輸層載入應用層報文上的任何資訊;
運輸層協議能提供的服務常常受制於底層網路層協議的服務型別,如果網路層協議無法為主機之間的通訊提供時延和頻寬保證的話,運輸層協議也就無法為程序之間傳送的應用程式報文提供時延或者頻寬保證;
因特網傳輸概述
因特網為應用層提供了截然不同的兩種傳輸層協議:UDP(使用者資料報協議)它提供一種不可靠、無連線的服務;另一種是TCP,它提供可靠的,面向連線的服務;運輸層分組也被稱為報文段;
簡單瞭解一下因特網的網路層,網路層協議有一個名字即IP,即網際協議。IP為主機間提供邏輯通訊,IP的服務模型為盡力而為交付服務(best-effort delivery service)這意味著IP盡最大的努力在主機間交付報文段,但是不做任何保證。它不保證報文段的交付、不保證報文段按序交付、不保證報文段中資料的完整性;即IP提供一種不可靠的服務;每臺主機都需要有一個網路層地址,即IP地址。
UDP和TCP最基本的責任就是將IP提供的主機間交付服務擴充套件到不同端系統上兩個個程序之間的服務。這也被稱為傳輸層的多路分解和多路複用;UDP和TCP通過在傳輸層首部新增差錯檢查欄位來提供完整性檢查。程序到程序之間的資料交付和差錯檢查是最低限度的兩種傳輸層服務,也是UDP可以提供的僅有的兩種服務。UDP和IP一樣,也是不可靠服務;
TCP提供額外的服務,首先它是一種可靠資料服務,這意味著TCP協議保證資料的按序、完整地從傳送端應用程序傳送到接收端應用程序;TCP通過序號、確認、定時器以及流量控制來將IP的不可靠資料傳輸轉換為可靠資料傳輸;其次,TCP提供擁塞控制,擁塞控制與其說是一種提供給應用程式的服務,不如說是一種提供給整個網路的服務,因為整個網路都將因為擁塞控制而受益;不太嚴格地說,擁塞控制力求為每一個經過一條擁塞網路的連線提供平等的共享網路鏈路頻寬,從而避免一條TCP連線用過多的流量來淹沒通訊主機之間的鏈路和裝置;擁塞控制是通過調節傳送進網路的的流量速率來做得到;UDP不提供擁塞控制;
一個提供可靠資料傳輸和擁塞控制的協議一定是一個複雜的協議,我們接下來會探求這樣的一個協議實現,但是在這之前,需要了解多路複用和多路分解這個概念;
多路複用和多路分解
有前面的介紹,我們可以知道,傳輸層將網路層提供的面向主機的邏輯通訊擴充為面向不同應用程序的邏輯通訊,並且這一過程稱為多路複用和多路分解;值得注意的是,多路複用和多路分解是每個計算機網路都需要的;
實際上,傳輸層和應用程式程序之間通過Socket(套接字)關聯,這樣通過Socket就可以區別同一主機上的不同應用程序,從而傳輸層提供服務變為可能;傳輸層從同一臺主機上的不同Socket接收資料的過程稱為多路複用;傳輸層向同一臺主機上的不同Socket傳輸資料的過程稱為多路分解;為了實現多路複用和多路分解,我們需要標誌套接字,並將相關資訊新增到報文段中。
實際上,每個套接字都有一個唯一的ID,被稱為埠號;而在傳輸層接收到來自應用程式的分組並通過新增傳輸層首部而形成報文段的過程中,該埠號被寫入;埠號大小在0-65535之間,其中0-1023屬於周知埠號,它們為特定的Socket而擁有;
無連線的多路複用與多路分解
需要注意的是,在建立Socket的時候,是由傳輸層為之分配埠號;一個UDP套接字是由一個目的IP地址和目的埠號即二元組來標誌的;如果兩個UDP報文段有不同的源IP地址或者源埠號,但是有相同的目的IP和目的埠號的話,它們將通過同一個Socket到達同一個應用程式,除非TCP報文段攜帶了初始建立連線的氫氣;
面向連線的多路複用與多路連線
TCP協議中的Socket是通過一個四元組來標記的:(源IP地址,源埠號,目的IP地址,目的埠號);兩個具有不同源IP地址或者源埠號,但有相同的目的IP地址和目的埠號的TCP報文段將通過兩個不同的Socket進入同一應用程序;這也表示,一個應用程序可以關聯多個Socket,而一個Socket將只關聯一個應用程序;常常,這樣的對應關係是通過執行緒來實現的:一個程序有多個執行緒,而每個執行緒關聯了一個Socket;這樣做可以提高伺服器效能嘛;
實際上,傳輸層就是根據這些資訊來實現多路分解的;而這些資訊是在多路複用的時候被放置在報文段中的;
無連線運輸UDP
一個不提供不必要服務的傳輸層協議是什麼樣的呢?什麼都不做?那是不行的,我們必須做一些什麼!前面提到過,差錯檢查和程序到程序的資料交付是傳輸層協議必須提供的功能,事實上,UDP的確做到了這些,並且。。只做了這些。它幾乎沒有對IP增強別的東西;因為在傳送報文段之前,傳送方和接收方的傳輸層實體之間沒有握手,所以UDP也被稱為無連線的;
DNS是一個使用UDP的應用層協議例子;UDP在接收到來自Socket的資料時,UDP為該報文新增首部欄位(源和目的埠號,以及其他兩個小欄位),然後將報文段交給網路層,網路層通過IP協議盡力地將該報文段交付,至於結果,盡力就好;當DNS客戶端等待不到對該查詢的響應時(有可能網路層將其丟失了)則會向其他Name Server傳送查詢請求,要麼就通知應用程式,做不到;
既然TCP提供了可靠資料傳輸,並且提供了擁塞控制,為什麼人們還需UDP呢?存在即合理,你覺得不合理是因為你還不能理解,但並不能改變其合理或者存在這一既定事實,除非你做些什麼。事實上,有些應用很適合UDP(因為沒有連線過程啊,因為不會受擁塞控制的調節啊,更自由唄);UDP有以下好處:
- 關於何時、傳送什麼資料的應用層控制更為精細:這是因為一旦應用程式將資料交給UDP,UDP就會打包將其傳送給網路層,不會受到傳輸層的調節,這在一些實時應用中比較實用;當然,應用程式還可以通過UDP+自主開發一些功能的模式來擴充套件UDP。
- 無需建立連線:所以就不會引入額外的時延。這也可能是DNS使用UDP而不是TCP的主要原因,如果使用TCP的話,DNS服務將會慢很多;HTTP使用TCP的主要原因是對TCP的可靠性的依賴超過對速度的要求;
- 無需維護連線狀態:TCP為了實現可靠資料傳輸和擁塞控制需要在端系統中維護一些引數,這些引數包括:接收和傳送的快取、擁塞控制引數、確認號和序號;這些引數資訊都是必須的;而UDP因為不建立連線,所以自然也就不需要維護這些狀態,這就減少了時空開銷;
- 分組首部更小:TCP有20位元組的首部開銷,而UDP只有8位元組;
這些應用程式使用了TCP作為其傳輸層協議:電子郵件、遠端終端訪問、Web、檔案傳輸;這些應用通常使用UDP作為其傳輸層協議:遠端檔案伺服器、網路管理(因為這裡應用即便在網路處於擁塞的情況下仍要工作,所以UDP更為合適)、路由選擇協議和名稱轉換(DNS);這些應用兩個都有使用:流式多媒體、因特網電話等多媒體應用;這些應用對實時性的要求較高同時對可靠性的要求又不是很高,所以既可以使用UDP也可以使用TCP協議。
不過在UDP之上執行多媒體應用是有爭議的,因為UDP沒有擁塞控制協議,所以其對網路有很大的威脅性:大量的UDP流量將使網路過度擁塞而造成TCP連線幾乎無法傳輸資料,並且因為網路擁塞,所以應用又有著較高的丟包率,而因為丟包率UDP很有可能繼續傳送資料,由此使得網路效率低下。也有研究人員提出一些新的機制,使得所有資料來源,包括UDP源執行自適應的擁塞控制來解決這一問題;
需要注意的是,使用UDP仍然可以實現可靠資料傳輸,只不過這一部分功能需要在應用程式中自主開發;將可靠性直接構建於應用程式中,將使其既可以可靠地傳輸資料又可以避免受制於TCP的擁塞控制(傳輸速率的控制);
UDP報文結構
UDP首部只有4個欄位,每個欄位佔用兩個位元組,分別是:源埠號、目的埠號、長度和校驗和;其中,長度表示包含首部在內的UDP報文段長度,以位元組為單位;校驗和欄位用來計算報文段在傳輸的過程中是否出現了差錯;一種常見的校驗和的計算方法是:傳送方將前三個欄位做按位加運算,然後將其取反作為校驗和;然後接收方對所有四個欄位(每個欄位16位)進行求和,如果沒有出現差錯,則最後的結果全是1,否則就表明出現了錯誤;出現錯誤的原因可能有:傳輸鏈路上資料受到干擾、資料儲存在中間路由器的時候,出現了錯誤;
UDP作為傳輸層協議,提供的差錯檢測功能很有可能和底層協議提供的相似功能產生冗餘;但是,這是必須的,因為由於不能保證源和目的地之間所有鏈路都提供差錯檢測功能,即便資料在鏈路上正確傳輸,也無法保證其在中間路由器的記憶體中不發生錯誤;所以要實現端到端的差錯檢測,就必須在傳輸層協議中實現該功能;這一原則在系統設計中被稱為端到端原則:“因為某一功能必須在端到端實現,與在較高層次提供這些功能的代價相比,在較低層次上設定的功能可能是冗餘的,或者根本是沒有用的”;
IP作為網路層協議,可以執行在任何第二層協議上,所以運輸層提供差錯檢測也是必須的;UDP可以檢測差錯,但是無法恢復差錯,能做的除了將其丟棄外,便是將其交給應用程式然後給出警告;
可靠資料傳輸原理
可靠資料傳輸的問題,不僅在傳輸層需要考慮,在鏈路層以及應用層都需要考慮這個問題,所以瞭解一些其基本原理是必要的,特別是在詳細瞭解TCP中為實現可靠資料傳輸而採取的。許多原理;
可靠資料傳輸為上層實體提供的服務抽象是:資料可以通過一套可靠的通道進行傳輸,藉助於可靠通道,傳輸資料就不會受到損壞或者丟失;並且所有資料都可以按照其傳送順序進行交付。而這正是TCP向呼叫它的應用所提供的服務模型;
實現這種抽象服務是可靠資料傳輸的責任,但是因為可靠資料傳輸的底層協議可能是不可靠的,所以這項任務有一點困難;
單方向的可靠資料傳輸流程大概是這樣的:可靠資料傳輸->不可靠資料傳輸->不可靠的傳輸通道->可靠資料接收->上傳Data;
一個可靠資料傳輸協議,將要面對以下問題:分組丟失、分組損壞到達、分組亂序到達;
- 分組損壞到達問題,可以通過校驗和來判斷是否發生損壞,如果損壞可以採用兩種方法:一種方法是由接收方就地恢復,這要求校驗和欄位包含足夠的資訊;另一種方法是接收方通知傳送方再次傳輸該分組,一般來說採用這種方法;這樣就需要有一個方法供接收方和傳送方就是否正確傳輸某一分組進行溝通,從而引入了ACK和NAK分組;
- 分組亂序到達問題,可以通過對分組編號來解決,這樣接收方就可以對下一個到達的分組產生“期望”;如果發生了亂序,接收方可以有兩種不同的解決方法,第一種,是簡單丟棄該分組,並告訴傳送方自己的期望;第二種是將該分組快取下來,然後等所有該到的分組都到了後,一起將資料向上層協議提交;第一種方法看起來就很愚蠢:都正確接收到了資料,何必要丟棄呢?第二種方法比較常用;
- 分組丟失問題,可靠資料傳輸採用“ACK/NAK”機制來解決:當傳送方發出編號為0的分組,接收方需要向傳送方回發一個ACK分組,表示接收方正確收到該分組;當分組損壞或者丟失或者超時的時候,接收方將向傳送方傳送對上一次接收到的分組的ACK,暗示傳送方,該到的沒到;
總體上來說,可靠資料傳輸面臨的問題就是這麼多,對不同問題所採取的解決方法不同導致了不同的可靠資料傳輸方案的實現策略;而總體上用的解決方法有:定時器、序號、肯定確認、否定確認、校驗和、視窗和流水線;視窗和流水線將在下面提到;
我嘗試著理清它們的關係:
根據傳送方一次傳送的分組數目,可以將可靠資料傳輸協議的工作模式分為停等協議和流水線;
停等協議
所謂停等協議是指傳送方傳送完分組A後,需要等待接收方對分組A的反饋分組,如果收到肯定分組,那麼就傳送下一個分組;如果收到否定分組,那麼就重新發送當前分組;值得注意的是,有些協議中只有肯定分組,肯定分組包含一個分組號K,表示標號為K的分組已經收到,傳送方可以傳送標號為K+1的分組;如果傳送方此時等待的正好是對K的確認分組,那麼傳送方就會發送標號為K+1的分組;如果傳送方接收到對K-1的確認,表示接收方並沒有收到編號為K的分組,需要重發;另外,停等協議中只需要一個定時器用來監聽超時事件;
停等協議的一個明顯問題就是效率低下,甚至可以作為網路協議限制網路硬體發揮性能的例子;
流水線協議
所謂流水線模式是指傳送方連續傳送M個分組,然後等待接收方對這M個分組的確認情況;如何處理髮送和收到的分組,這要看流水線模式的具體策略,因為這涉及到差錯恢復方法,而在流水線模式下,差錯恢復方法有兩種,一種是回退N步,一種是選擇重傳;
回退N步
在回退N步中,傳送方維護一個N——視窗大小和一個base——傳送方期待收到的最小待確認分組序號,同樣也是視窗的起點,還有一個next Sequence變數,表示上層需要傳送分組時,可以使用的序號。這樣全部序號就被劃分為0-base-1,這一部分的分組是已傳送且收到接收方確認的分組,base-next Sequence-1這一部分的分組是已傳送但是尚未收到確認的,其中base是尚未收到確認的最小序號;next-1到base+N-1表示當前傳送方可以使用的序號,表示一種傳送能力;當傳送方收到確認號為base的確認分組後就會向前移動視窗,所以回退N步也被稱為滑動視窗協議;
這是傳送方需要維護的資料,同時傳送方需要響應的事件有:上層呼叫、收到ACK、超時事件;
- 上層呼叫:檢查next Sequence是否在視窗之內,如果在,這說明發送方還有傳送能力,傳送之;
- 收到ACK:回退N步策略對序號為n的分組採取累積確認的方式,即當收到序號為n的ACK時,表明序號小於等於n的分組全部到位;傳送方收到的ACK畢竟來自接收方,收到ACK的情況還得看接收方如何傳送;
- 超時事件:如果發生超時事件,那麼傳送方會重發所有已傳送但是未確認的分組,即分組號在base和next sequence-1之間的所有分組;這也是為什麼叫“回退N步”,如果收到一個ACK,則定時器會重行啟動;如果沒有待確認的分組,定時器將被終止;
在接收方,如果到達分組的序號為n且該分組是按序到達,那麼傳送ACK,這就導致傳送方移動視窗;如果不是按序到達,那麼接收方丟棄所有失序分組;丟棄一個正確接收的失序分組可能會導致更多的重傳;
選擇重傳
回退N步協議存在一個問題就是當視窗和頻寬的時延都較大時,單個分組的差錯可能會引起GBN重傳大量的分組,然後許多本來不用重傳的分組會充斥在通道中,造成資源浪費;選擇重傳就是讓傳送方僅重傳那些丟失和受損的分組而避免不必要的重傳;
傳送方需要響應的時間仍然有三個:
- 上層呼叫:基本上同GBN一致;
- 收到ACK:如果該分組號在base-next sequence-1之間,將其標記為已到;如果等於base,則移動視窗到最下的待確認的分組序號處;在GBN中,接收方根本不會收到非base的ACK,但是怎麼收,還的看怎麼發;
- 超時事件:同GBN不一樣的是,選擇重傳需要為每一個分組建立一個定時器,如果某個已傳送但未被確認的分組超時,傳送方重發該分組;
接收方將確認一個正確接收的分組而不管其是否按序;失序的分組被快取,直到形成連續資料後將其提交給上層;值得注意的是,如果接收方收到了已經確認的分組,則說明確認ACK丟失,或者時延太長,接收方和傳送方溝通不及時;這也表明了關於那些分組到位了,那些分組還沒到位,接收方和傳送方有著不一樣的檢視;
另外還需要注意的是,序號的重用問題,如果在分組中序號欄位的位數為k,那麼最大的序號為2^k-1,所以有可能不同分組同時佔用一個序號,為了避免這種情況,需要做的是控制分組的生命週期,總之,問題是存在的,解也是存在的;
面向連線的TCP
TCP連線
TCP協議之所以被稱為是面向連線的協議,是因為在一個應用程序可以向另一個應用程序傳送資料前,這兩個程序將首先“握手”,即它們必須交換一些預報文段,已建立對關於資料傳輸的引數的共識;作為TCP連線建立的一部分,通訊雙方都將初始化與TCP連線的許多相關變數;
TCP的連線,並不是一條像電路交換網路中端到端的TDM、FDM電路,而不是一條虛電路;其連線狀態被端系統所維護而中間路由器完全忽略了該協議,中間路由器看到的只是資料,也就是說,TCP只執行在端系統之上;所以,TCP連線更像一種狀態而不是物理的、實際的連線;
TCP提供全雙工服務,並且是點對點的,資料從A到B的同時,也能從B到A;TCP協議無法提供“多播”服務,一條TCP連線只關聯一個傳送方和接收方(當然,傳送方也是接收方);
對於TCP建立過程中的“握手”階段,需要明白的是,手一共握了三次,前兩次報文段不承載“有效負載”,第三次握手的時候,報文段是可以裝載“有效負載”的;這個過程是這樣的:通訊的發起方首先發送一個特殊的TCP報文段給接收方,這是第一次握手;接收方收到該報文段後,對該報文段進行響應,此為第二次握手;傳送方接收到響應報文段後,傳送第三個報文段,其中包含了有效負載;因為TCP建立的過程,一共發生了三次握手,所以該過程也被稱為“三次握手”;
當TCP連線建立後,兩個應用程序就可以傳送資料了。應用程式將要傳送的資料通過Socket傳遞給TCP,TCP將資料引導到該連線的傳送快取,傳送快取大小是在三次握手的過程中確定的;之後TCP將時不時從該快取中拿出資料進行傳送,一個有趣的事情是,TCP規範中沒有規定TCP應該在何時傳送快取裡的資料,描述為“TCP應該在它方便的時候以報文段的形式傳送資料”;TCP每次可以從快取中傳送的最大資料長度稱為MSS(Maximum Segment Size)。一般來說,MSS+TCP/IP首部的長度要小於等於鏈路的MTU(即鏈路層最大幀長度Maximum Transport Unit)而乙太網和PPP的MTU都等於1500位元組,TCP/IP的首部通常為40位元組,所以MSS一般來說為1460位元組;
TCP為每塊客戶資料加上TCP首部後就形成了一個個TCP報文段;這些TCP報文段被交給網路層,然後被髮送到網路中;當TCP報文段到達接收端時,便進入了接收端的快取,等待被應用程式讀取;
TCP報文段結構
TCP報文段結構,從整體上來說由首部+資料欄位組成;其中資料欄位來自應用層,其長度不能大於MSS;首部的常規長度為20位元組,但是值得注意的是,TCP首部是可變長的;TCP首部是以32位元為單位組織的,其結構組成如下圖:
-
源埠號和目的埠號
這兩個資料用於TCP的多路複用和多路分解;分別為16位;
-
序號
該資料被用於實現可靠資料傳輸之按序到達,在一個TCP連線中,算是一個報文段的id,同時該id還指示了其所承載的資料的位置資訊;佔32位;
-
確認號
該資料表示接收方已經正確接收的報文段的序號,在流水線的差錯恢復方案裡,不同的恢復策略有不同的意義:回退N步裡,當傳送方接收到對K的確認號時,表示所有序號小於K的報文段均已到達;而在選擇重傳裡,則僅表示序號為K的報文段被正確接收;
-
首部長度
TCP的首部是可變長的,所以該欄位表示報文段的首部長度,也揭示了應用資料的開始位置;該欄位以32位元為單位,佔4位元(所以表示TCP首部的最大長度為31*32位元,大小需要自己算啦~);
-
選項欄位
該欄位用於在傳送方和接收方之間協商MSS的大小,在高速網路環境下,也可用於調節視窗大小;
-
標記欄位
ACK位表示確認號欄位的裡的值是否有效,如果ACK被置位,那麼該報文段就對確認號所指示的報文段進行了確認;
RST、SYN和FIN位用於TCP的連線和拆除;
PSH被置位時,指示接收方應該立即將資料交給上層;
URG被置位時表示報文段裡存在著傳送端的上層實體置為緊急的資料;緊急資料的最後一個位元組由16位緊急指標指出。當緊急資料存在並且給出了指向緊急資料尾指標時,TCP必須通知接收端的上層實體;
然而,實際上,PSH、URG和緊急資料指標在實踐中並沒有被使用;標記欄位一共6位元;
-
視窗大小
該欄位用來指示報文段傳送方視窗的大小——視窗大小在一定程度上指示了快取的空間大小;
序號和確認號
TCP報文段中兩個重要的欄位是確認號和序號;這兩個欄位是TCP實現可靠資料傳輸的重要部分;TCP將資料看作是一個無結構、有序的位元組流;值得注意的是,TCP的序號是基於傳輸的位元組流之上,而不是報文段的序列之上;也就是說,來自應用層的資料被TCP包裝在多個報文段中,其中第2個報文段的序列號不是2,而是1001,如果MSS為1000。關於確認號,如果採取回退N步策略,那麼TCP採用一種累計確認的方法,前面已經提到過,這裡就不贅述;一條TCP連線可以採取任意數字作為初始序號,這樣可以減少將那些殘存在網路中的報文段誤認為是新建連線的報文段(新舊連線恰巧採用了相同埠);
總體來說,一個報文段的序號就是該報文段資料欄位首位元組的序號;確認號就是接受主機正在等待接收的資料的下一個位元組序號;值得注意的是,服務端對接收端發來的報文段的確認被裝載到一個從服務端發往到接收端的報文段中,這種確認被稱為“捎帶”;
往返時間的估計與超時
TCP使用超時重傳機制來處理報文段的問題;雖然這在概念上很簡單,但是一個問題是,如何設定超時時間?如何設定超時時間,取決於網路的狀態,所以需要做的是估計網路的狀態。TCP使用一種Sample RTT的方法來估計RTT。Sample RTT就是從某報文段發出到收到對該報文段的確認之間的時間量。大多數TCP的實現是在某個時刻做一個Sample RTT測試。TCP並不為已經重發的報文段做Sample RTT測試,它只為傳輸一次的報文段測量Sample RTT。
TCP一般來說通過Estimated RTT=(1-a)Estimated RTT+a*Sample RTT來計算因路由器的擁塞和端系統負載變化所導致變化的RTT。a一般取1/8;因為Estimated RTT表示最近的網路狀況,所以其理應得到較大的權值;這種方法也被稱為指數加權移動平均;
除了估計RTT外,計算RTT的變化也是ok的,DevRTT =(1-b)DevRTT+b*|Sample RTT-Estimated RTT|;其中b的推薦值為0.25;當Sample RTT變化較大的時候,DevRTT的值較大,當Sample RTT變化較小的時候,DevRTT就較小;
TCP是如何考慮超時時間的呢?該時間因略大於測量的RTT,不易過小——容易引起不必要的重傳,也不易過大——網路對於報文段丟失情況的反應就會變慢;最後TCP採用瞭如下計算方式:Timeout Interval=Estimated RTT+4*Dev RTT;
可靠資料傳輸
IP協議提供的是盡力而為的服務:不保證不丟失、不保證按序到達、不保證沒有損壞,TCP協議在IP協議之上,提供可靠資料傳輸,從而保證一個程序從其相關聯的快取中讀取的資料和另一端程序傳送的資料是一致的;TCP使用超時重傳和冗餘確認技術來處理超時、丟失等情況;使用確認、序號等技術來保證按序到達;使用校驗和來檢驗是否報文段在傳輸過程中是否發生了錯誤;
超時時間加倍
在大多數TCP實現中,當發生超時事件時,超時時間並不是從Estimated RTT和Dev RTT推算出來而是直接將超時時間設定為原來的兩倍;然而,每當定時器在另兩個事件(收到ACK和接收到上層應用資料)發生時,新的超時時間將由上面提到的兩個值計算出來;實際上,這是一種形式受限的擁塞控制;
快速重傳
響應超時事件,然後重傳尚未收到確認的報文段,但是,當超時時間過長的時候,會顯著增加端到端的延遲;一種可行的方法是對冗餘ACK的的檢測;在理解冗餘ACK之前,需要先看一下接收方為什麼會發送冗餘ACK。接收方接收到某個報文段時,會檢查該報文段是否是按序到達,如果不是,那麼接收端會發送對已經收到的最後一個連續報文段的確認,所以如果傳送方收到冗餘ACK,說明有多個報文段到達了接收端,但不是接收端所期望的——這意味著,很有可能發生了丟失。所以傳送方可以在定時器過時之前快速重傳所丟失的報文段;
是回退N步還是選擇重傳?
首先,我們需要明白的是,TCP採用了累計確認的機制,也就說,如果接收方正確接收了某一失序到達的分組,那麼接收方傳送的ACK將是對最後接收的按序到達的分組的確認,而不是對剛剛接收的分組的確認;當然,許多TCP實現都會快取失序的分組;那麼問題來了,發生超時事件後,GBN將重傳所有待確認的分組,而不是丟失的分組;而選擇重傳會好很多;
對TCP提出的一種修改意見是所謂的選擇確認——即接收方對失序到達的分組也會確認,當該機制和重傳機制相結合使得TCP更像選擇重傳,於是TCP的差錯恢復協議最好被分類為GBN和SR協議的混合體;
流量控制
流量控制是一個速度匹配服務:TCP連線的傳送方和接收方都各自維護一個快取,因此兩者的資料交換應該在一個合理的速度範圍內:不讓對方發生資料溢位;TCP為它的應用程式提供了這種服務:流量控制服務。雖然流量控制和擁塞控制所採取的動作非常相似,但是它們的目的很明顯並不同。在接下來的討論中,我們將假設TCP是這樣實現的,即TCP接收方丟棄失序到達的報文段;
在TCP首部中有一個視窗大小欄位,TCP連線的雙方通過該欄位來向對方表明自己的視窗大小,即快取空間的大小;同樣,在TCP連線的兩端,各自維護著相關的變數:last Sent、last Acked;在傳送方,這兩個變數之間的分組就是已經發送但是尚未確認的分組;而在接收方,last Read表示應用程序下一次讀取的資料,last Revd表示最後納入快取的報文段編號(注意,我們討論的前提是TCP會將失序到達的報文段丟棄哦~);通過這些變數以及報文段首部中視窗大小欄位,我們就可以對傳送速度做一些控制:在傳送方last Sent-last Acked應該小於等於接收方的視窗大小;在接收端A=last Received-last Read就是已經使用的空間大小,所以視窗大小=buffer-A;
實際上,流量控制是一種對傳送方的限制措施吧,但是因為TCP是一種全雙工的協議,所以接收方同時也是傳送方;
對了,還有一個問題就是,如果接收方的視窗大小為0,那麼傳送端該如何處理呢?一個需要注意的事實是,接收方在沒有ACK或者資料要向傳送端傳送的時候,是不會通知傳送方其視窗大小已經改變,即如果應用程式讀取了快取中的資料,傳送方是不會知道的,除非它向接收方傳送了資料,而傳送方對其進行了確認;實際上,傳送方也是這麼做的!當接收到視窗大小為0的報文段後,傳送方會向接收方間隔傳送只有一個位元組的資料;這樣,雙方的通訊就不會被中斷啦;
連線管理
TCP連線管理包括如何建立和拆除一條TCP連線;TCP的建立很重要,因為它是網路時延的重要組成部分;當然,TCP連線的建立過程被稱為“三次握手”而拆除過程被稱為“四次揮手”——形象地描述了兩個過程中的資料交換情況;
建立過程
- 客戶端TCP傳送SYN報文段,所謂SYN報文段就是SYN位被置1的特殊報文段,它不包含應用層資料;同時會將一個隨機的序號放置於序號欄位;
- 包含SYN報文段的IP資料報到達服務端,服務端發現這是一個SYN報文段後,將視情況決定是否同意該建立TCP的連線請求,如果同意,那麼服務端將為之分配必要的快取空間和變數,然後準備傳送ACK報文段,首先服務端讀取其中的序號,將該序號+1後設置為ACK報文段的確認號,然後SYN位被置1,然後服務端選擇自己的初始序號,將其放到序號欄位;允許建立TCP連線的報文段也被稱為SYNACK報文段;當然,這裡還是沒有應用層資料;
- 在收到SYNACK報文段後,發起端也將分配快取和變數,同時客戶主機將向伺服器傳送另外一個報文段,該報文段對SYNACK進行確認,此時SYN位被置0;同時,該報文段可以攜帶客戶資料;
以上就是TCP連線建立的過程,因為一共傳送了三個分組,所以也被稱為“三次握手”;
拆除過程
- (客戶端關閉)客戶TCP傳送一個特殊報文段到伺服器,該報文段中FIN位被置1;
- 伺服器收到後向傳送方回送一個ACK報文段;
- (伺服器端關閉)伺服器端傳送FIN報文段給客戶端;
- 客戶端傳送ACK對該報文段進行確認;
然後,TCP連線就拆除了。
擁塞控制原理
擁塞原因與代價
計算機網路擁塞的原因是因為網路中的分組太多,而鏈路頻寬和路由器快取容量都是有限的;
- 當分組的到達速率接近鏈路容量時,分組將經歷巨大的排隊時延;
- 傳送方必須執行重傳已補償因為快取溢位而丟棄的分組;
- 傳送方遇到大時延時所進行的不必要重傳呼應器路由器李永奇鏈路來轉發不必要的分組。
- 當一個分組沿著一條路徑被丟棄時,每個上游路由器用於轉發該分組到丟棄該分組而使用的傳輸容量最終被浪費掉了;
擁塞控制方法
總體來說,我們可以更具網路層是否為傳輸層擁塞控制提供了顯式幫助來區分擁塞控制方法:端到端擁塞控制和網路輔助擁塞控制;
在端到端擁塞控制方法中,網路層並沒有向傳輸層擁塞控制提供顯式支援,即便網路中存在擁塞,端系統也必須通過對網路行為的觀察(如分組丟失與時延)來判斷;TCP必須通過端到端的方法解決擁塞控制,因為IP層不會像端系統提供有關網路擁塞的反饋資訊。TCP報文段的丟失(超時或者收到3次冗餘確認而得知)被認為是網路擁塞的一個跡象,TCP將相應地減小視窗長度;
在網路輔助的擁塞控制方法裡,網路層會向傳送方提供關於網路中擁塞狀態的顯式反饋訊息;比如使用一個位元位來指示網路是否擁塞;擁塞資訊從網路反饋到傳送方一般有兩種方式,其中直接反饋資訊可以由網路路由器傳送給傳送方,這種方式的通知通常採用一種擁塞分組的形式;第二種形式的通知是路由器標記或者更新從傳送方到接收方的分組中的某個欄位來指示擁塞的產生,然後由接收方向傳送方通知該網路發生了擁塞。
TCP擁塞控制原理
TCP必須使用端到端的擁塞控制而不是網路輔助的擁塞控制,因為IP並不會向端系統提供顯式的網路擁塞反饋;TCP所採用的方法是讓每一個傳送方根據其所感知的網路擁塞程度來限制其能向連線傳送流量的速率;如果TCP判斷網路通暢,那麼它會提高發送速率,如果TCP判斷網路擁塞,那麼它會限制傳送速率;需要解決三個問題:TCP如何限制傳送速率?TCP如何感知網路擁塞程度?TCP該以何種演算法改變其傳送速率?
我們知道,TCP連線的雙方都維護著兩個視窗,其中一個是作為傳送方的視窗,也被稱為擁塞視窗,它對傳送方能向網路中傳送流量的速率進行了限制,last sent-last acked<=min{cwnd,rwnd};另一個自然是作為接收方的接收視窗。
我們假設,傳送方可以在RTT時間範圍內連續傳送cwnd個位元組的資料,所以傳送速率即為cwnd/RTT;傳送方通過調整視窗大小來對傳送資料的速率加以控制;(回答如何控制速率);
我們將TCP傳送方的丟包事件定義為:要麼超時,要麼收到接收方的3個冗餘ACK;如果網路擁塞,那麼網路中的路由器就會發生快取溢位,進而導致資料報被丟棄,然後就會引起傳送方的丟包事件;此時,TCP傳送方就可以認為TCP連接出現了擁塞;(回答如何感知網路擁塞程度);
另外,TCP將接收方傳送的ACK視為網路通暢的標誌,如果ACK到達的速率較高,那麼TCP的擁塞視窗就會以較高的速率擴大,如果ACK到達的速率較慢,那麼TCP擁塞視窗的增加速度也會較慢;因為TCP使用ACK對擁塞視窗做出調節,所以也別稱為自計時的;
TCP傳送速率過高,網路就很容易擁塞;TCP傳送方如果過於謹慎,那麼就無法充分利用網路的頻寬;所以TCP如何設定自己的傳送速率,才能使得網路不會擁塞而且還充分利用頻寬呢?關於這個問題,TCP使用下列指導性原則回答這些問題:
- 一個丟失的報文段意味著擁塞,因此當丟失報文段時應當降低TCP傳送方的速率;
- 一個確認報文段指示該網路正在向接收方交付傳送方的報文段,因此,當收到對先前報文段的確認時,可以增加發送方的速率;
- 頻寬檢測;TCP調節器傳輸速率的策略是增加其速率以響應到達的ACK,除非出現丟包,此時才減少傳送速率;以為網路中沒有明確的擁塞控制狀態信令,ACK和丟包事件充當了隱式訊號;
以上為TCP擁塞控制的概述,接下來就是廣受讚譽的TCP擁塞控制演算法,該演算法包含三個主要部分:慢啟動、擁塞避免、快速恢復;慢啟動和擁塞控制是TCP的強制部分;兩者的差異在於對收到的ACK做出反應時增加cwnd長度的方式;快速恢復是推薦部分,對於TCP傳送方並非是必須的;
慢啟動
TCP連線在開始的時候,其cwnd常設定為一個MSS,然後在慢啟動狀態每收到一個ACK,cwnd就增加一個MSS;這樣的話,在慢啟動階段,傳送速率是指數增加的(1,2,4,8…);
何時結束這種指數增長?有三種情況:傳送了超時、發生了冗餘ACK以及cwnd達到ssthresh。ssthresh是慢啟動閾值的速記;在慢啟動階段,如果發生了超時事件,那麼ssthresh就被設定為當前cwnd的一半,然後將cwnd置為1;當cwnd逐步增加到ssthresh時,再翻倍增加cwnd就有一點魯莽了,所以此時TCP結束慢啟動,進入擁塞避免模式。在擁塞避免模式裡,TCP將更謹慎地增加cwnd;如果收到冗餘ACK,那麼TCP會做一次快速重傳,然後進入快速恢復階段;
擁塞避免
一旦進入擁塞避免狀態,cwnd的值大約是上次遇到擁塞時的一半,所以TCP在每個RTT中,只將cwnd增加一個1個MSS大小;也就是說在擁塞避免階段,cwnd是線性增加的;
當出現超時時,TCP將cwnd設定為1,然後將ssthresh更新為cwnd的一半;當收到冗餘ACK時,TCP將cwnd減半,然後將ssthresh置為cwnd值的一半,並且進入快速恢復狀態;
快速恢復
在快速恢復階段,對於引起TCP進入該狀態的缺失報文段,每收到一個ACK,cwnd增加一個MSS;最終,當對丟失報文段的一個ACK到達時,TCP降低cwnd後進入擁塞避免狀態;如果出現超時事件,快速恢復在執行如同慢啟動和擁塞避免中相同動作後,進入慢啟動狀態;