無連接運輸的UDP、可靠數據傳輸原理、面向連接運輸的TCP


由[RFC 768]定義的UDP只是做了運輸協議能夠做的最少工作。除了復用/分解功能極少量的差錯檢測外,它幾乎沒有對IP增加別的東西。如果應用程序開發人員選擇UDP而不是TCP,則該應用程序差不多就是直接與IP打交道。UDP從應用程序進程得到數據,附加上用於多路復用/分解服務的源和目的端口號字段,以及兩個其他小字段,然后形成的報文段交給網絡層。網絡層將運輸層報文段封裝到一個IP數據報中,然后盡力而為地嘗試將此報文交付給接收主機。如果該報文段到達接收主機,UDP使用目的端口號將報文段中的數據交付給正確的應用程序進程。

值得注意的是,使用UDP時,在發送報文段之前發送方和接收方的運輸層實體之間沒有握手。正因為如此,UDP被稱為時無連接的。

DNS是一個通常使用UDP的應用層協議的例子。當一台主機中的DNS應用程序想要進行一次查詢時,它構造了一個DNS查詢報文並將其交給DUP。無需執行任何與運行在目的端系統中的UDP實體之間的握手,主機端的UDP為此報文添加首部字段,然后將形成的報文段交給網絡層。網絡層將此UDP報文段封裝進一個IP數據報中,然后將其發送給一個名字服務器。在查詢主機中的DSN應用程序則等待對該查詢的響應。如果它沒有收到響應(可能是由於底層網絡丟失了查詢或響應),則要么試圖向另一個名詞服務器發送該查詢,要么通知調用的應用程序它不能獲得響應。

為什么開發人員寧願在DNS目錄服務器上使用UDP構建應用,而不選擇TCP上構建呢?既然TCP提供了可靠數據傳輸服務,而UDP不能提供,那么TCP是否總是首選呢?答案是否定的,因為有許多應用更適合用UDP,原因主要有:

  • 關於何時、發送什么數據的應用層控制更為精細。所以主要着重關注將數據及時發送出去的應用程序更適合采用UDP協議,因為TCP有握手的往返延時,還有阻塞控制機制,而且TCP不管交付時間需要用多久都會將數據發送出去且確認接收方是否接收到。而像時時通訊(視頻/語音)這種應用並不需要TCP這樣的特性。
  • 無需建立連接。上面說過TCP傳輸數據之前需要握手會有往返延時,但是像Web應用傳輸數據就非常有必要采用TCP協議,特別時HTML這樣的主文件。
  • 無連接狀態。不維護連接狀態相比維護連接狀態更節約網絡資源,服務器可服務對象越多,一般UDP支持更多活躍用戶的應用。
  • 分組首部開銷小。每個TCP報文段都有20字節的首部開銷,而UDP僅8字節的開銷。

所以像網絡管理(SNMP)、路由選擇協議(RIP)、名字轉換(DNS)。括號對應的是應用層協議,這些應用程序都在運輸層采用了UDP協議。接着來看UDP報文段的結構,在此之前有必要說明以下,UDP的應用是可以實現可靠數據傳輸的。可以通過應用程序自身建立可靠性機制來完成,將可靠性建立於應用程序中可以使其“左右逢源”,也就是說應用進程可以進行可靠通信,而無需受制於由TCP擁塞控制機制和傳輸速率限制。

 一、UDP報文段結構

 

應用層數據占用UDP報文段的數據字段。UDP首部只有四個字段,每個字段有兩個字節組成。通過端口號可以使目的主機將應用數據數據交給運行在目的端系統中的響應進程(即執行分解功能)。長度字段指示UDP報文段中的字節數(首部加數據,字節為單位)。因為數據字段的長度在一個UDP段中不同於在另一個段中,故需要一個明確的長度,接收方使用檢驗和來檢查該報文段中是否出現差錯。實際上,計算檢驗和時,除了UDP報文段以外還包括IP首部的一些字段,為了討論檢驗和的計算,暫時忽略。

UDP檢驗和:

UDP檢驗和提供了差錯檢測功能。也就是說,檢驗和用於確定UDP報文段從源到達目的地移動時,其中的比特是否發生了改變。發送方的UDP對報文段中的所有16比特字的和進行反碼運算,求和時遇到的任何溢出都被回卷。得到的結果被放在DUP報文段中的檢驗和字段。如何計算UDP/TCP檢驗和

 雖然UDP提供差錯檢測,但它對差錯恢復無能為力,UDP的某種實現只是丟棄受損的報文段,其他市縣是將受損的報文段交給應用程序並給出警告。

 二、可靠數據傳輸原理

考慮可靠數據傳輸的問題,不僅僅是在運輸層,也會出現在鏈路層。可靠數據傳輸的框架,為上層提供的服務抽象是:數據可以通過一條可靠的信道進行傳輸。借助於可靠信道,傳輸數據比特就不會受到損壞或丟失,而且所有數據都按照其發送順序進行交付。這恰好就是TCP向調用它的因特網應用所提供的服務模型。

TCP是在不可靠的(IP)端到端網絡層之上實現的可靠數據傳輸協議,更一般的情況是,兩個可靠通信端點的下層可能是由一條物理鏈路組成或是由一個全球互聯網絡組成。就可靠數據傳輸目的而言,可以將較低層直接視為不可靠的點對點信道。底層信道損壞比特或丟失整個分組時,需要什么樣的協議機制,這貫穿我們討論始終假設分組將以它們發送的次序進行交付,某些分組可能會丟失,這就是說,底層信道不會對分組重排序。

 

上圖簡單的模擬傳輸協議接口。(rdt表示可靠數據傳輸協議,_send指示rdt的發送端正在表調用。udt表示不可靠的數據傳輸)

rdt_send()函數表示可以調用傳輸協議的發送方;

udt_send()表示發送端和接收端發送分組給對方;

deliver_data()表示rdt向叫高層交付數據的方法;

rdt_rcv()表示rdt從底層信道接收一個分組;

 2.1構造可靠傳輸協議

現在一步步地研究一系列協議,它們一個比一個復雜,最后得到一個無錯、可靠的數據傳輸協議。首先,考慮最簡單的情況,即底層信道是完全可靠的,將此協議定義為rdt1.0。

1.0 經完全可靠信道的可靠數據傳輸:rdt1.0

 

 上圖表示發送方和接收方的有限狀態機(Finite- State Machine, FSM),上圖(左)中的FSM定義了發送方的操作,上圖(右)中FSM定義了接收方的操作。

 上面的流程圖描述了rdt1.0發送端的處理過程。

 上面的流程圖描述了rdt1.0接收端的處理過程。

雖然在rdt1.0中是基於一個理想中完全可靠信道來實現的數據傳輸,但是也描述了運輸層的基本功能就是負責網絡層與應用層的數據傳輸,我們將這個運輸層協議先成為簡單協議。在簡單協議中,一個單元數據與一個分組沒有差別,而且所有分組是從發送方流向接收方,有完全可靠信道,接收不需要任何反饋信息給發送方。注意,我們也假定了接收方與發送方的速率一樣快。

2.0 經具有比特差錯信道的可靠數據傳輸:rdt2.0

底層信道實際的模型是分組中的比特可能受損,分組的傳輸、傳播或緩存的過程中,這些比特差錯通常出現在網絡物理部件中。在討論比特差錯信道的可靠數據傳輸時,我們假設所有發送的分組還是按照順序接收的。相較完全可靠信道只有增加比特差錯這一種情況。

從理論上來講,只需要將比特差錯的分組識別出來,然后將差錯信息反饋給發送方,讓發送方將差錯分組重新發送一次。在計算機網絡中,基於這樣重傳機制的可靠傳輸一些被稱為自動重傳請求協議(ARQ)。ARQ協議中還需要另外三種協議功能來處理存在比特差錯的情況:

  • 差錯檢測:基於檢驗和的比特差錯識別。(在《計算機網絡》第五章有詳細的差錯檢測和糾錯技術分析)
  • 接收方反饋:當接收到正常分組時反饋“肯定確認”(ACK),當接收到比特差錯分組時反饋“否定確認”(NAK),用0表示NAK,1表示ACK。
  • 重傳:接收方收到有比特差錯的分組,發送方將重新傳遞該分組文。

理論上來說,好像有ARQ協議就可以實現比特差錯信道的可靠數據傳輸。發送方每傳輸一個分組給接收方,等待接收方反饋回信息,如果返回的是ACK就繼續傳輸下一個分組,如果返回的是NAK,就重新傳輸上一個分組,這種行為又被稱為停等協議。到這里,好像rdt2.0就可以實現經具有比特差錯信道的可靠數據傳輸了,別高興太早,仔細看看反饋信息是什么?它本身也是被信道傳輸的比特,怎么才能保證反饋信息的比特不出錯呢?這是不可能的,因為物理傳輸受損是必然的,那要怎么解決呢?

考慮處理受損ACK和NAK的三種可能性:

  • 第一種方法(重復應答):當發送方收到的反饋信息比特受損無法識別時,發送方請求接收方再重復一次反饋。這種可能性有一個非常極端的情況就是如果重復發送的反饋還是丟失比特呢?又或者是發送方發送重復反饋請求時丟失比特呢?這就陷入了死循環。
  • 第二種方法:增加足夠的檢驗和比特,使發送方不僅可以檢測差錯,還可以修復差錯。對於會產生差錯但不丟失分組的信道,這就可以直接解決問題。
  • 第三種方法:當發送方收到含糊不清的ACK或者NAK分組時,只需要重傳當前數據分組即可。然而,這種方法在發送方到接收方的信道中引入了冗余分組。冗余分組的根本困難在於接收方不知道它上次發送的ACK或NAK是否被正確收到,因此它無法事先知道接收到分組是新的還是一次重復重傳。

解決這個新問題(第三種方法中的無法確認分組問題)的方法非常簡單,在數據分組中添加一個新字段,讓發送方對其數據分組編號,即將發送數據分組的序號放到該字段。重傳的分組序號與最近接收到的分組序號相同,新的分組序號會變化(使用模2“前向”移動:模2運算)。目前我們假定的是信道不會丟失分組,所以ACK/NAK分組本身不需要指明他們要確認的分組序號。發送方接收到ACK/NAK分組是為響應最近發送的數據分組而生成的。rdt2.1反映出目前正在發送的分組或希望接收的分組的序號是0還是1。rdt2.1使用了從接收方收到的肯定確認和否定確認。當接收方收到失序分組時,發送肯定確認。如果收到受損分組,則發送否定確認。如果不發送NAK,而是對正確接收到的分組發送一個ACK,那么也能得到與NAK一樣的效果。發送方收到對同一個分組的兩個ACK(接收到了冗余ACK)后,就可以知道接收方沒有正確接收被確認兩次的分組后面的分組。這也產生了協議rdt2.2。rdt2.2是在有比特差錯信道上實現的一個無NAK的可靠傳輸協議,此時ACK報文就需明確所確認的分組序號。(這一段的具體實現不太明白《計算機網絡》P141~143)。

3.0 經具有比特差錯的丟包信道可靠數據傳輸:rdt:3.0

現在除了比特損壞外,底層信道還會丟包,這樣的情況並不罕見。所以協議必須關注兩個問題:怎么檢測丟包以及發生丟包后該做些什么?

在rdt2.2中已經使用的技術有檢驗和、序號、ACK分組、和重傳等。在這個基礎上考慮結局丟包問題,丟包所造成的問題就是接收方無法響應,不會反饋信息給發送方,這里就有了一個突破口,就是延時時間,假設已知在不丟包的情況下接收方的反饋時間是n,那就可以采用倒計數定時器等待時間n后再次發送上次的分組,這同樣會產生冗余數據分組,而這樣的問題再rdt2.2中已經得到解決,所以丟包問題也就解決了。

3.1 流水線可靠數據傳輸協議 :rdt3.1

在rdt3.0中的以及2.2中都采用了停等協議,對信道的利用率非常的低(案例見《計算機網絡》P144~146)。解決這個問題的簡單方案就是:流水線可靠數據傳輸協議。流水線可靠數據傳輸協議采用發送多個分組(同樣序號的一個分組發送多個)的方式來實現。

多個分組的運輸原理是基於信道實際容量和單個分組傳輸的使用比率來實現的,也就是假設使用停等協議傳輸方式只是用了信道傳輸容量的33%,那多分組傳輸就可以一次連續傳輸三個相同序列的分組,這樣信道利用率就提高了3被,也就提高了分組的傳輸成功率。這樣產生的連鎖反應就是成功率提升降低了重傳,降低了重傳就降低了反饋比特損壞和丟失率,整體性能就會提高很多。

流水線可靠數據傳輸協議除了多分組傳輸,當然就是采用流水線的傳輸方式連續傳輸,那什么是流水線傳輸呢?為什么要使用流水線傳輸呢?

采用多分組傳輸實現了非常貼近完全可靠傳輸,就不必要采用停等應答的低效方式來解決比特損壞和分組丟失,而是采用回退N步和選擇重傳的方式來解決比特損壞和分組丟失問題。在解析回退N步和選擇重傳的具體技術之前,先來看看流水線完全可靠協議的基本實現邏輯:

從上面的流水線可靠傳輸協議示圖中可以看到每一個分組都會被發送多次,這是流水線可靠傳輸協議的核心,只要多個分組中有一個沒有損壞就可以實現了可靠傳輸。但是,也可能會出現極端的情況就是整組損壞的情況,在示圖中我標識了重傳反饋,其實不正確,只是為了區分以下整組丟失、反饋比特損壞導致的超時,其實質上都是使用選擇重傳來解決。在解析選擇重傳之前需要先來說明以下回退N步(GBN)協議的原理,這是因為流水線可靠傳輸協議帶來的下列影響造成的:

  • 必須增加序號范圍,因為每個輸送中的分組(不計重傳的)必須有一個唯一的序號,而且也許有多個在輸送中未被確認的報文。
  • 協議的發送方和接收方兩端也許必須緩存多個分組。
  • 所需序號范圍和對緩沖的要求取決於數據傳輸協議如何處理丟失、損壞及延時過大的分組。解決流水線差錯恢復的兩種基本辦法是:回退N步(Go-Back-N,GBN)選擇重傳(Selective Repeat,SR)

3.1.1 回退N步

在回退N步(GBN)協議中,允許發送方發送多個分組而不需要等待確認,但是也受限於在流水線中未確認的分組數不能超過某個最大允許數N。在流水線可靠協議示圖中可以看到GBN協議的序號范圍。如果將基號(base)定義為最早的未確認分組序號,將下一個序號(nextseqnum)定義為最小的未使用序號,則可以將序號范圍分為四段:在[0,base-1]段內的序號對應於已發送並被確認的分組;[base,nextseqnum-1]段內對應已發送但未被確認的分組;[nextseqnum,base+N-1]段內的序號用於可以被立即發送的分組。如果有數據來自上層的話,最后大於base+N的序號不能被使用,知道當前流水線中未被確認的分組已得到確認。

隨着協議進行,該窗口序號空間向前滑動,因此N常被稱為窗口長度,GBN協議也常被成為滑動窗口協議。在此我們限定的窗口長度N,而不是讓分組數為無限大是有兩個原因①流量控制②TCP擁塞控制。如果分組序號字段的比特數為k,那么序號范圍就是[0,2^k-1],在一個有限的序號范圍內,所有涉及序號的運算必須使用模2^k運算。

GBN發送方必須響應三種類型的事件:

  • 上層的調用:當上層窗口調用發送時,發送方首先檢查發送窗口是否已滿(即是否有N個已發送但未被確認)。如果窗口未滿,則產生一個分組並將其發送,並更行響應變量。如果窗口已滿,發送方只需要將數據返回給上層,隱式指示上層該窗口已滿。然后上層可能會等一會兒再試。實際實現中,發送方可能會緩存這些數據,或者使用同步機制允許上層在僅當窗口不滿時才調用發送方法。
  • 收到一個ACK:在GBN協議中,對序號為n的分組的確認采用積累確認的方式,表明接收方以正確接收到序號n的以前且包括n在內的所有分組。
  • 超時事件:回退N步來源於出現丟失和延時過長時的處理行為,就像停等協議中那樣,如果接收方的反饋時間超出發送方的定時器時間(分組丟失損壞,反饋比特損壞,網絡延時長),發送方就會重新發送已發送當未被確認的分組,這就是回退N步的來源。

 下圖表示了GBN的運行模式:

GBN協議中綜合了可靠數據傳輸協議構件的所有技術,這些技術包括使用序號、積累確認、檢驗和超時/重傳操作。采用GBN這種協議看似好像很合理很實用,但是卻有一個非常大的閉端,隨着協議的運行,重傳的分組就會積累的越多,看到上面的示圖就一目了然,后面的重傳分組和正常分組都一樣多了,而且隨着時間推移,重傳會更加頻繁,所以后面就有了選擇重傳來解決這個問題。

3.1.2 選擇重傳

 GBN也存在着一些性能問題,單個的分組出現差錯就會引起GBN重傳大量不必要重傳的分組。在信道差錯率很高時,流水線可能會被不必要重傳的分組所充斥。可操作此處SR Java小程序查看SR運作流程。
  SR協議通過讓發送方僅重傳那些它懷疑在接受方出錯(丟失或受損)的分組而避免不必要的重傳。
  下圖(來自《計算機網絡 自定向下方法》)顯示了SR協議中發送方和接收方的序號范圍。
  在SR協議中發送方和接收方所采取得動作可見下文描述。

  SR發送方的事件與動作

  • 從上層接收到數據
    從上層接收到數據后,SR發送方檢查下一個可用於該分組的序號。如果該序號位於發送方的窗口內,則將數據打包並發送;否則就像再GBN中一樣,要么將數據換組,要么返回給上層以便以后傳輸。

  • 超時
    定時器在此用來防止丟失分組。但是,SR中每個分組都要有自己的邏輯定時器。

  • 收到ACK
    如果收到ACK,倘若該分組序號在窗口內,SR發送方就將那個被確認的分組標記為已接收。
    如果該分組的序號等於send_base,則窗口基序號向前移動到具有最小序號的未被確認分組處。
    如果窗口移動了並且有序號落在窗口內的未發送分組,則發送這些分組。

  SR接收方的事件與動作
  SR接收方將確認一個正確接收的分組而不管其是否按序。失序的分組將被緩存,知道所有丟失分組皆被收到為止,這時才將一批分組按序交付給上層。

  • 序號在[rcv_base,rcv_base+N-1]內的分組被正確接收
    在此情況下,收到的分組落在接收方的窗口內,一個選擇ACK被回送給發送方。
    如果該分組以前沒有被接收到過在,則緩存該分組。
    如果該分組的序號等於基序號,則該分組以及以前緩存的序號連續的分組交付給上層。
    然后,接收窗口按向前移動分組的編號向上交付這些分組。

  • 序號在[rcv_base-N,rcv_base-1]內的分組被正確收到
    在此情況下,必須產生一個ACK,即使該分組時接收方以前已確認的分組。
    要意識到這是非常重要的,假設接收方以前就接收了send_base分組,現在接收到重傳的不回送ACK,那么發送方窗口就永遠也不會向前滑動!
  • 其他情況,忽略該分組

  SR協議中發送方窗口和接收方窗口並不總是一致的。這會引出關於序號范圍的一個問題。
  在有限的序號范圍的實現里,如果SR接收方窗口太大並且發送方和接收方窗口間的不同步,便會造成發送方這邊新分組序號與舊分組序號重復使用。導致無法辨析該序號代表的是一個新分組還是舊分組。
  SR的窗口長度應限制在小於等於序號空間大小的一半。

小結一下可靠數據傳輸機制中使用到的技術:
  檢驗和、定時器、序號、確認、否定確認、窗口/流水線

 三、面向連接的運輸:TCP

TCP被稱為面向連接的協議,這是因為在一個應用進程可以開始向另一個應用進程發送數據之前,這兩個進程必須先相互“握手”,即它們必須相互發送某些預備報文段,以建立確保數據傳輸的參數。作為TCP連接建立的一部分,連接的雙方都將初始化與TCP連接相關的許多TCP狀態變量。這些變量將會是控制實際數據傳輸的重要邏輯變量,在介紹TCP實際運輸邏輯之前,先來看一下TCP協議執行發送和接收的基本結構(緩存)示圖:

看到上面這個圖你一定會很驚訝,為什么與博客的第二部分“可靠數據傳輸協議”差別那么大?這是一個很關鍵的問題,在第二部分中說的可靠數據傳輸協議都是已分組傳輸為傳輸基本單元,但是需要注意的是,在TCP協議中傳輸的基本單元是用數據流來描述的,雖然本質上都是報文段,但是這兩個描述差別將是TCP協議一切的起源(個人理解),從分組到數據流其主要的差別就是分組是從應用層直接調用運輸層的接口實現,然后就直接被用來傳輸,在第二部分中出現的冗余是直接采用丟棄的方式來解決,甚至當分組出現錯序的后面所有分組都被丟棄,這種粗暴的做法必然帶來的就是增加了網絡傳輸壓力,浪費傳輸資源,結果就是傳輸效率低。而TCP協議采用了緩存的方式來解決這個問題,當然可靠數據傳輸的其他技術:檢驗和、定時器、序號、確認、否認確認、窗口/流水線、重傳機制都被TCP作為基礎而應用。實質上,TCP協議就是在寫基礎技術進一步優化,以達到可靠數據傳輸的最佳應用方案。

TCP提供的是全全雙工服務(full-duplex-service),並且TCP連接也是點對點(piont-to-piont)的,這就說明TCP連接是單個發送方與單個接收方之間的連接。在一次發送操作中,從一個發送方將數據傳給多個接收方,即“多播”操作對TCP來說是不可能的。

先就示圖的基本邏輯來分析TCP從連接到數據傳輸的過程:

  • 客戶首先發起一個特殊的TCP報文段,服務器另一端特殊的TCP報文段響應,最后,客戶再用第三個特殊報文段作為響應。前兩個報文段不承載“有效負荷”,也就是不包含應用層數據;而第三個報文段承載有效載荷。這個連接過程也叫做三次握手
  • 當數據被應用層通過套字節傳遞到運輸層,TCP協議運行控制數據流,將數據引導到該鏈接的發送緩存里。發送緩存是在三次握手初期設置的緩存之一。
  • 接這TCP從緩存中取出有最大限制長度的數據(MSS)加上報文首部合成報文發送給接收方。

最大限制長度的數據(MSS):通常根據最初確定的由本地發送主機發送的最大鏈路層幀長度(最大傳輸單元)來設置。設置最大傳輸單元主要依據以太網和PPP鏈路層的最大鏈路層幀決定。假設鏈路層最大鏈路幀是1500字節的MTU,而TCP報文首部長度通常是40字節,因此MSS的典型值為1460字節。

 3.1 TCP報文段結構

TCP報文段的結構與UDP一樣,首部包括端口號和目標端口號,它被用於多路復用與多路分解或用來將數據送到上層的應用層。報文的數據部分就不解釋了,就是上層應用層的報文,這里重點來關注TCP的首部結構。

數據偏移:TCP中數據的開始處距離TCP報文段的起始位置有多遠 == TCP報文段的首部長度。表示長度以32位比特為單位,因此最大可以表示60字節(15*4)的首部。保留占位6以后使用。

標志字段 含義
URG URG=1,用來指示報文段里存在着被發送端的上層實體置為“緊急”的數據。此時緊急指針有效
ACK 當ACK=1時,確認號字段有效,表示對已被成功接收的報文段的確認
PSH 當PSH=1時,指示接收方應立即將數據交付給上層,不用等接收緩存滿了才交付
RST RST和下面的SYN、FIN用於TCP建立連接和釋放連接。
RST用於①RST=1,TCP連接初出現嚴重差錯,必須釋放連接然后重新建立運輸連接
②拒絕一個非法報文段或者拒絕打開一個連接
SYN 在TCP連接建立時用來同步序號
FIN FIN=1,釋放TCP連接

 序號與確認號:

在開始介紹TCP時我就重點的描述了TCP的緩存和字節流,TCP把數據看成無結構的、有序的字節流。從TCP的序號上就可以看出這一點,序號是建立在字節流上,而不是建立在報文字段的序列之上,報文的序號是該報文首字節的字節流編號。假設數據流由一個包含500000字節的文件組成,其MSS為1000字節,數據流的首字節編號是0,TCP將該數據流構建500個報文段,給第一個報文段分配序號0,第二個報文段分配序號1000,第三個報文段分配序號2000,以此類推。

將字節編號作為序號有什么好處呢?接着來看確認號,假設由A、B兩個主機,A向B請求了一個報文段編號為0~1000的所有字節,但是在發送的過程中由於網絡層和鏈路層的問題,A只接收到了0~536序號字段和900~1000的序號字段,這就出現了亂序的問題,通常解決這類問題有兩個基本選擇:1、接收方立即丟棄失序字段2、接收方保存失序字段,並等待缺少的字節以填補間隔。顯然第二種方法更有效,而這第二種方法就需要確認號來完成這樣精確的問題,TCP會先將1~536序號的字段添加到字節流中,然后將900~1000的序號字段暫時緩存,並將536序號作為確認好反饋給B,這時候B就知道了應該從發送方的緩存中取出537字節開始的字節流給接收方發送,直到發送發送到899字節流序號的報文,接收方將900~1000的字節流合並到字節流中,然后反饋確認號1000表示數據全部接收完畢。

 3.2 往返時間的估計與超時

在第二部分的可靠數據傳輸協議中介紹了超時/重傳機制,同樣,TCP協議也應用了同樣的機制,但是在第二部分並沒有就超時/重傳的具體時間做任何討論,TCP作為一個具體的可靠數據傳輸協議當然就必須對超時/重傳的時間設定有一個明確的解決方案。RTT作為往返時間,超時設定值可能就要比RTT大。TCP的實現僅在某一時刻測量一次往返時間(SampleRTT),而且不會測量重傳報文的往返時間([Kan 1987])。

由於路由器的擁塞和端系統負載的變化,這些報文段的SampleRTT值會隨之波動,所以並非一個典型值。為了估計一個典型的RTT,TCP協議采用了對SampleRTT取平均值的辦法。一旦獲得一個新SampleRTT時,TCP就會根據下列公式來更新Esti-matedRTT:

  EstimatedRTT = (1 - a ) * EstimatedRTT + a * SampleRTT

EstimatedRTT的新值是由以前的Esti-matedRTT的值與SampleRTT新值加權組合而成的。在[RFC 6298]中給出的a參考值是a = 0.125(即:1/8),這時上面的公式變為:

  EstimatedRTT = 0.875 * EstimatedRTT + 0.125 * SampleRTT

通過加權平均是為了得到一個更能反映網絡當前的擁塞情況,統計學的觀點講,這種平均被稱為指數加權移動平均。在TCP中除了估算RTT外,測量RTT的變化也是有價值的。[RFC 6298]定義了RTT偏差DevRTT,用於估算SampleRTT一般會偏離EstimatedRTT的程度:

  DevRTT = (1 - b) * DevRTT + b * | SampleRTT - EstimatedRTT |

注意DevRTT是一個SampleRTT與EstimatedRTT之間差值的EWMA。如果SampleRTT值波動較小,那么DevRTT就會很小。反之,如果波動很大,那么DevRTT的值就會很大。b的推薦值為0.25。

設置和管理重傳超時間隔:

 假設已經給出了EstimatedRTT值和DevRTT值,那么TCP超時間隔應該用什么值呢?超時間隔應該大於等於EstimatedRTT,否則,將造成不必要的重傳,但是超時間隔也不能比EstimatedRTT大太多,否則當報文段丟失時,TCP不能很快地重傳該報文段,導致傳輸延時過大。而且當SampleRTT值波動較大時,這個余量應該大些。所以DevRTT就有用武之地了,計算重傳超時間隔的公式:

  TimeoutInterval = EstimatedRTT + 4 * DevRTT

在[RFC 6298]中推薦TimeoutInterval初始值為1秒,同樣當出現超時后,TimeoutInterval值將加倍,以免即將被確認的后繼報文段過早出現超時,一旦報文段收到更新的EstimatedRTT后,TimeoutInterval就又使用上述公式計算。

 3.3 可靠數據傳輸

TCP在IP不可靠的情況下盡力為服務創建一個可靠數據傳輸服務。TCP的可靠數據傳輸服務確保一個進程從其接收緩存中讀出的數據流是無損壞、無間隔、非冗余和按序的數據流,即該字節流與連接的另一方端系統發送出的字節流是完全相同的。TCP提供可靠數據傳輸的方法在博客的第二部分有詳細的技術功能描述。

TCP具體是如何提供可靠數據傳輸的其有三個主要事件:從上層應用程序接收數據、定時器、收到ACK。實際上其基本的技術都是建立博客第二部分描述的可靠數據傳輸協議的技術上,但是TCP相對第二部描述的技術應該進一步優化,下面來一段簡單的TCP可靠數據傳輸處理流程和多種情況:

情況一:假設有A、B兩個主機,A主機向B主機發送一個報文,報文段的序號是92,且含8個字節的數據。在發送該報文段之后,主機B准確的接收到了該報文的所有數據並給A主機發送了確認報文序號100。但是,確認報文在傳輸途中丟失,引發了A主機重發這段報文,並且也順利的傳輸到了B主機,但是B主機原本就已經有了這段報文,所以直接丟棄重傳的報文段。

情況二:A主機在給B主機發送第一段報文后,由於窗口長度允許繼續發送后續報文,所以A主機又給B主機發送了第二段報文,序號是100,包含20個字節的數據。假設這兩個報文都完好的被B主機接收,但是B主機給A主機發送的第一段確認報文丟失,導致A主機啟動重發,並刷新定時器,如果第二段確認報文順利的在定時器刷新后的間隔事件內被A主機收到,A主機就不會發送第二段報文了,這就是TCP的接收緩存的作用。

情況三:同樣是情況二的數據傳輸,並且第一段確認報文丟失,但是第二段確認報文在第一段報文間隔時間之前就被A收到了,A也啟動第一段報文的重發機制了。

超時間隔加倍:

超時間隔加倍這種機制為了控制擁塞,這種方式不只是存在TCP協議中,在以太網的CSMA/CD中也同樣采用這種方式控制網絡擁塞。這里還不詳細介紹TCP的擁塞控制,后面會有詳細TCP擁塞控制機制分析。但是在上面的TCP重傳中已經引述出了超時間隔加倍,為了方便理解,就在這里闡述一下超時間隔加倍的原理機制。

比如在上面的例子中提到反饋報文丟失,TimeoutInterval就不是用EstimatedRTT和DevRTT的值來推算了,而是直接在原來的TimeoutInterval上加倍,並且如果第一次超時加倍后還是沒有收到反饋報文,發送方在超時后重傳報文段並且在第一次加倍的基礎上再次加倍。(初始值:0.75秒,第一次超時:1.5秒;第二次超時:3秒)。這樣就有效的防止了過多的分組被傳入從源到目的端之間的路勁上,導致更大的網絡延遲。

快速重傳:

超時觸發重傳存在的問題是超時周期可能相對較長,在這個時間里接收方可能會將超時報文段前一個確認報文重復反饋給發送方多次,這時就會出現ACK冗余現象,如果在超時周期之內一個報文段的ACK冗余數量得到三個,發送方就不會等超時間隔來觸發重傳了,而是提前實現重傳。這就是快速重傳。([RFC 5681])

TCP是回退N步還是選擇重傳

因為TCP的緩存機制,不需要回退N步全部重傳,而是將超時或觸發快速重傳的報文段進行重傳操作,重觸發機制上來看很像GBN協議,但是並沒有像GBN那樣回退N步,而是有選擇的重傳操作,所以也可以說是SR協議,但是SR協議會向窗口傳入疑似丟包或比特損壞的報文段,這與快速重傳非常類似,但是又並行存在超時重傳的GBN機制。TCP協議獲得了SR協議與GBN協議的優勢,所以可以說是GBN協議與SR協議的混合體。

 3.4 流量控制

 這里所謂的流量控制並不是控制網絡流量MSS,也不是擁塞機制,而是控制TCP緩存的流浪,TCP的緩存空間不能溢出。這里需要關注的幾個值:

LastByteRead:接收方的應用程序從TCP緩存中讀出的最后一個字節符。

LastByteRcvd:接收方從網絡中接收並放入緩存的最后一個字節符。

LastByteSent :發送方傳入網絡的最后一個字節符。

LastByteAcked:發送方發送的字節符但未被接收方確認放入緩存中的最小字節符。

RcvBuffer:接收方的緩存空間。

rwnd:接收方空閑的緩存空間。

所以得出以下公式:

LastByteRcvd - LastByteRead <= RcvBuffer ------TCP不允許已分配的緩存溢出。

rwnd = RcvBuffer - [ LastByteRcvd - LastByteRead] ------- 接收方空閑的緩存空間等於分配緩存空間 - 已被占用的緩存空間(已被占用的緩存空間等於已讀最后一個字節符減去已放入緩存的最后一個字節符)

LastByteSent - LastByteAcked <= rwnd ------ 發送方最后發送的最后一個字節符減去未被接收方放入緩存的最小字節符要小於接收方的可用緩存空間

 3.5 TCP連接管理

關於TCP連接其實關於連接部分在前面已經多次出現了,指示解除連接沒有介紹過,但是其本質上是確認機制實現的線程釋放。也沒有太多東西,直接上圖:

三次握手:客戶端向服務端請求資源。

第一步:客戶端向服務端發送一段特殊報文,請求連接。(標志位SYN設置為1;)

第二部:服務端接收到請求連接報文后,返回一段特殊報文給客戶端,建立連接。(SYN被設置為1;確認字段被加一:client_isn + 1;)

第三步:客戶端收到服務端的連接建立確認報文后,客戶端帶着請求資源必須要的數據(也可以不帶數據)發送一段特殊報文給服務端。(確認字段被加一:client_isn + 1;(在前面的基礎之上加一))

上面三個步驟就是三次握手的過程,正式開始傳輸數據的時候SYN會被設置為0,接着來看四次揮手過程,也就是釋放連接的過程:

第一步:當客服端請求的數據全部接收到以后,客戶端就會向服務端發送一個釋放連接的報文段。報文中的FIN被設置為1。

第二部:服務端收到客戶端的釋放連接報文段后,向客戶端發送一個確認報文段ACK。

第三步:服務端緊接着有向服務端發送一個釋放連接的報文段,並且報文中的FIN也會被設置為1。

第四步:客戶端接收到服務端的釋放報文段后,又向服務端發送一個確認報文端ACK,告訴服務端收到了釋放連接的確認報文。

 四、擁塞控制原理

 4.1 出現擁塞

網絡擁塞通俗的說就是網絡傳輸需求大於網絡傳輸能力,網絡擁塞的三個一般性情況:

情況一、假設路由有無限大的緩存空間,發送端的發送速率好像就可以無限擴大,但是並非如此,鏈路完全中的排隊分組也就會隨着發送端的發送速率無限擴大,由於排隊分組達到鏈路層的容量時,分組就會產生很大的排隊延時,從而導致網絡擁塞。

情況二、假設路由的容量是有限的,也就是當路由的緩存已滿后就會丟棄后續被傳入的分組,由於丟棄分組就會導致運輸層重傳,這就會進入一個惡性循環,丟棄越多,重傳就會越多,延時就會越長,延時越長就會觸發更多的重傳,這種網絡擁塞也可以說是超出供給載荷。

情況三、多個發送方在多個路由器之間的多跳路徑,當其中一個路由其中了大量的傳輸容量時,途徑這個路由的所有傳輸都會被擁塞。

4.2 擁塞控制方法

端到端擁塞控制:在這個方法中,網絡層沒有為運輸層擁塞控制提供顯式支持,即使網絡中存在擁塞,端系統也必須通過對網絡行為的觀察來推斷網絡是否阻塞。TCP必須通過端到端的方法解決擁塞控制,因為IP層不會向系統提供有關網絡擁塞的反饋信息。TCP報文的丟失(通過超時或3次冗余確認而得知)被認定是網絡擁塞的一個跡象,TCP會相應的減少其窗口長度。在TCP擁塞控制的一些最新建議中,即使用增加往返時延值作為網絡擁塞程度增加的指示。

網絡輔助的擁塞控制:在網絡輔助的擁塞控制中,網絡層構件(即路由器)向發送方提供有關網絡中擁塞狀態的顯示反饋信息。擁塞信息從網絡反饋到發送方通常有兩種方式:①直接反饋信息由網絡路由器發給發送方,這種通知方式常采用了一種阻塞分組(choke packet)的形式(含義為:“我阻塞了”)②顯示擁塞通知(Explicit Congestion Notification,ECN)。第二種是路由器標記或更新熊發送方流向接收方的分組中的某個字段來指示擁塞的產生。當接收方接收到這樣的分后后,就會向發送方發送網絡擁塞的通知。這種方式需要至少需要一個完整的RTT。使用網絡輔助的擁塞控制例子可參見ATM ABR擁塞控制。

4.3 TCP擁塞控制

在TCP的擁塞控制機制上,TCP使用的是端到端的擁塞控制。即讓每個發送方根據感受到的網絡擁塞程度限制其能向其連接發送流量的速率。由此引發三個問題:1.TCP發送方如何限制它向其連接發送流量的速率。2.TCP發送方如何感知它到目的地之間的路徑出現了擁塞。3.當發送方感受到了端到端的時延使用何種算法來改善其發送速率。

4.3.1 TCP發送方如何限制它向其連接發送流量的速率?

TCP連接的每一個端都有一個接收緩存、一個發送緩存、和幾個變量組成。運行在發送方的TCP擁塞控制機制跟蹤一個額外的變量,即擁塞窗口(cwnd)TCP發送方通過cwnd來控制發送流量速率,也就是未被確認的數據流量不能超過cwnd,所以也必然不會超過rwnd(接收方空閑緩存空間)。

  LastByteSent - LastByteAcked <= min{cwnd, rwnd}

上面這個公式就是通過擁塞窗口控制發送方的發送流量速率的基本原理,假設rwnd無限容量,但是發送流量速率受到已發送發送未確認分組(已發送的最大字節符序號減去未被確認的最小字節符序號)的控制,也就間接的限制了發送流量速率。擁塞窗口長度校驗算法

所以發送方的發送流量速率可以估算為:在不考慮丟包和發送時延的情況下,發送速率大概是cwnd/RTT(字節/秒)。

4.3.2 TCP發送方如何感知它到目的地的路徑出現了擁塞?

假設將一個TCP發送方的“丟包事件”定義為:要么出現超時,要么收到來自接收方的3個冗余ACK。當出現過度的擁塞時,沿着這條路徑上的一台路由緩存就會溢出,引發數據包被丟棄,丟棄數據包引發的丟包事件要么超時或收到3個上一個報文段的冗余ACK。所以發送方就認為在發送方到接收方的路徑上出現了擁塞

從判斷窗擁塞中可以反向思考,如果發送方傳輸的報文段都正常被接收方接收到,也就是說都能正常的接收到接收方反饋的確認報文ACK,這樣的情況下就可以被判定為時網絡傳輸通暢,發送方就會增加擁塞窗口的長度,從而達到提高傳輸速率的效果,而卻會根據反饋確認報文的ACK的RTT來判斷網絡通暢程度,如果確認已相當慢的速率到達就,速率增長也同樣會以相當慢速度增長。反之,如果確認相對快的速率到達,擁塞窗口就會瞬間增大窗口長度。TCP把這種機制叫做自計時

4.3.3 當發送方感受到了端到端的時延使用何種算法來改善其發送速率?

 概述了TCP擁塞控制后,現在就需要采用對應的方法來改善發送速率,廣受贊譽的TCP擁塞控制算法[Jacobson 1988]。該算法包含了三個重要的部分:1、啟動慢;2、擁塞避免;3、快速恢復。慢啟動和擁塞控制是TCP的強制部分,兩者的差異在於對收到的ACK做出反應時增加cwnd長度的方式。慢啟動比擁塞控制能更快的增加cwnd的長度。快速恢復是推薦部分,對TCP發送方並非必須的。

4.3.3.1 慢啟動

在慢啟動狀態,cwnd的值以一個MSS開始並當傳輸的報文首次被確認就增加一個MSS。如下圖所示,開始發送一個報文段,收到確認后擁塞窗口增加1。然后傳輸2個報文段,收到2個確認后增加擁塞窗口變成了4個MSS。這樣沒經過一個RTT,發送速率就會翻番。於是,TCP發送的起始速率慢,但是在慢啟動階段會以指數增長

但是這樣會的增長何時終止呢?慢啟動對這個問題提供了幾種答案。

  • 第一種: 如果出現一個有超時引起的丟包事件(即網絡中出現了擁塞),TCP發送方將cwnd設置為1並重新開始慢啟動過程。它還會將第二個狀態變量ssthresh(“慢啟動閾值”)設置為cwnd/2。

  • 第二種: 與ssthresh相關。當增加到cwnd=ssthresh時,結束慢啟動並開始擁塞避免。

  • 第三種: 如果檢測到3個冗余ACK,這時TCP執行快速重傳進入快速恢復狀態。

4.3.3.2 擁塞避免

進入擁塞避免狀態后,cwnd的值大約是上次遇到擁塞時的值的一般,即距離擁塞可能並不遙遠。TCP可定不能再像慢啟動那樣對cwnd的值翻番,而是采用了另一種方式更新cwnd的值。當TCP發送方無論何時達到一個新的確認,就將cwnd增加一個MSS字節(MSS/cwnd)。例如,如果MSS是1460個字節並且cwnd也是1460字節,則在一個RTT內發送10個報文段。每個到達ACK增加1/10MSS的擁塞窗口長度,因此,在收到所有10個報文端的確認后,擁塞窗口的值將增加一個MSS。

那擁塞避免的線性增長在什么時候停止呢?跟啟動慢一樣,再出現丟包事件后cwnd的值同樣會被更新為cwnd值的一半。如果丟包事件是由三個冗余ACK事件觸發,在這種情況TCP會進入快速恢復狀態。

4.3.3.3 快速恢復

在前面的TCP可靠數據傳輸部分就有提到快速重傳,相當於是快速恢復的開始。在快速恢復中,對引起TCP進入快速恢復狀態的缺失報文段,對收到的每個冗余的ACK,cwnd的值增加一個MSS。最終,當丟失報文段到達時,TCP再降低cwnd后進入擁塞避免狀態。如果出現超時事件,cwnd置為1個MSS,並且ssthresh置為cwnd的一半,遷移到慢啟動。

快速恢復是TCP推薦部件而不是必需。一種早期的TCP版本TCP Tahoe,不管是超時而引起的丟包還是3個冗余ACK引起的丟包事件,都會將cwnd置為1個MSS,並進入慢啟動階段。TCP較新的版本TCP Reno綜合了快速恢復算法。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM