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

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

值得注意的是,使用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擁塞控制機制和傳輸速率限制。服務器

 1、UDP報文段結構

 

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

UDP檢驗和:併發

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

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

 2、可靠數據傳輸原理

考慮可靠數據傳輸的問題,不只僅是在運輸層,也會出如今鏈路層。可靠數據傳輸的框架,爲上層提供的服務抽象是:數據能夠經過一條可靠的信道進行傳輸。藉助於可靠信道,傳輸數據比特就不會受到損壞或丟失,並且全部數據都按照其發送順序進行交付。這剛好就是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的窗口長度應限制在小於等於序號空間大小的一半。

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

 3、面向鏈接的運輸: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的序號字段,這就出現了亂序的問題,一般解決這類問題有兩個基本選擇:一、接收方當即丟棄失序字段二、接收方保存失序字段,並等待缺乏的字節以填補間隔。顯然第二種方法更有效,而這第二種方法就須要確認號來完成這樣精確的問題,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、擁塞控制原理

 4.1 出現擁塞

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

狀況1、假設路由有無限大的緩存空間,發送端的發送速率好像就能夠無限擴大,可是並不是如此,鏈路徹底中的排隊分組也就會隨着發送端的發送速率無限擴大,因爲排隊分組達到鏈路層的容量時,分組就會產生很大的排隊延時,從而致使網絡擁塞。

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

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

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]。該算法包含了三個重要的部分:一、啓動慢;二、擁塞避免;三、快速恢復。慢啓動和擁塞控制是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綜合了快速恢復算法。