參考資料
http://blog.chinaunix.net/uid-26275986-id-4109679.html
http://network.51cto.com/art/201501/464002_all.htm
一、滑動窗口協議
將TCP與UDP這樣的簡單傳輸協議區分開來的是它傳輸數據的質量。TCP對於發送數據進行跟蹤,這種數據管理需要協議有以下兩大關鍵功能:
可靠性:保證數據確實到達目的地。如果未到達,能夠發現並重傳。
數據流控:管理數據的發送速率,以使接收設備不致於過載。
要完成這些任務,整個協議操作是圍繞滑動窗口確認機制來進行的。因此,理解了滑動窗口,也就是理解了TCP。
TCP面向流的滑動窗口確認機制:
TCP建立連接的初始,B會告訴A自己的接收窗口大小,比如為‘20’
TCP將獨立的字節數據當作流來處理。一次發送一個字節並接收一次確認顯然是不可行的。即使重疊傳輸(即不等待確認就發送下一個數據),速度也還是會非常緩慢。

TCP消息確認機制如上圖所示,首先,每一條消息都有一個識別編號,每一條消息都能夠被獨立地確認,因此同一時刻可以發送多條信息。設備B定期發送給A一條發送限制參數,制約設備A一次能發送的消息最大數量。設備B可以對該參數進行調整,以控制設備A的數據流。
為了提高速度,TCP並沒有按照字節單個發送而是將數據流划分為片段。片段內所有字節都是一起發送和接收的,因此也是一起確認的。確認機制沒有采用message ID字段,而是使用的片段內最后一個字節的sequence number。因此一次可以處理不同的字節數,這一數量即為片段內的sequence number。
TCP數據流的概念划分類別
假設A和B之間新建立了一條TCP連接。設備A需要傳送一長串數據流,但設備B無法一次全部接收,所以它限制設備A每次發送分段指定數量的字節數,直到分段中已發送的字節數得到確認。之后,設備A可以繼續發送更多字節。每一個設備都對發送,接收及確認數據進行追蹤。
如果我們在任一時間點對於這一過程做一個“快照”,那么我們可以將TCP buffer中的數據分為以下四類,並把它們看作一個時間軸:
1. 已發送已確認 數據流中最早的字節已經發送並得到確認。這些數據是站在發送設備的角度來看的。如下圖所示,31個字節已經發送並確認。
2. 已發送但尚未確認 已發送但尚未得到確認的字節。發送方在確認之前,不認為這些數據已經被處理。下圖所示14字節為第2類。
3. 未發送而接收方已Ready 設備尚未將數據發出,但接收方根據最近一次關於發送方一次要發送多少字節確認自己有足夠空間。發送方會立即嘗試發送。如圖,第3類有6字節。
4. 未發送而接收方Not Ready 由於接收方not ready,還不允許將這部分數據發出。

接收方采用類似的機制來區分已接收並已確認,尚未接受但准備好接收,以及尚未接收並尚未准備好接收的數據。實際上,收發雙方各自維護一套獨立的變量,來監控發送和接收的數據流落在哪一類。
Sequence Number設定與同步:
發送方和接收方必須就它們將要為數據流中的字節指定的sequence number達成一致。這一過程稱為同步,在TCP連接建立時完成。為了簡化假設第一個字節sequence number是1,按照上圖示例,四類字節如下:
1. 已發送已確認字節1至31。
2. 已發送但尚未確認字節32至45。
3. 未發送而接收方已Ready字節46至51。
4. 未發送而接收方Not Ready字節52至95。
發送窗口與可用窗口:
整個過程關鍵的操作在於接收方允許發送方一次能容納的未確認的字節數。這稱為發送窗口,有時也稱為窗口。該窗口決定了發送方允許傳送的字節數,也是2類和3類的字節數之和。因此,最后兩類(接收方准備好而尚未發送,接收方未准備好)的分界線在於添加了從第一個未確認字節開始的窗口。本例中,第一個未確認字節是32,整個窗口大小是20。
可用窗口的定義是:考慮到正在傳輸的數據量,發送方仍被允許發送的數據量。實際上等於第3類的大小。左邊界就是窗口中的第一個字節(字節32),右邊界是窗口中最后一個字節(字節51)。概念的詳細解釋看下圖。

可用窗口字節發送后TCP類目與窗口大小的改變:
當上圖中第三類的6字節立即發送之后,這6字節從第3類轉移到第2類。字節變為如下:
1. 已發送已確認字節1至31。
2. 已發送但尚未確認字節32至51。
3. 未發送而接收方已Ready字節為0。
4. 未發送而接收方Not Ready字節52至95。

確認處理以及窗口縮放:
過了一段時間,目標設備向發送方傳回確認信息。目標設備不會特別列出它已經確認的字節,因為這會導致效率低下。目標設備會發送自上一次成功接收后的最長字節數。
例如,假設已發送未確認字節(32至45)分為4段傳輸:32-34,35-36,37-41,42-45。第1,2,4已經到達,而3段沒有收到。接收方只會發回32-36的確認信息。接收方會保留42-45但不會確認,因為這會表示接收方已經收到了37-41。這是很必要的,因為TCP的確認機制是累計的,只使用一個數字來確認數據。這一數字是自上一次成功接收后的最長字節數。假設目標設備同樣將窗口設為20字節。
當發送設備接收到確認信息,則會將一部分第2類字節轉移到第1類,因為它們已經得到了確認。由於5個字節已被確認,窗口大小沒有改變,允許發送方多發5個字節。結果,窗口向右滑動5個字節。同時5個字節從第二類移動到第1類,5個字節從第4類移動至第3類,為接下來的傳輸創建了新的可用窗口。因此,在接收到確認信息以后,看起來如下圖所示。字節變為如下:
1. 已發送已確認字節1至36。
2. 已發送但尚未確認字節37至51。
3. 未發送而接收方已Ready字節為52至56。
4. 未發送而接收方Not Ready字節57至95。

每一次確認接收以后,這一過程都會發生,從而讓窗口滑動過整個數據流以供傳輸。
處理丟失確認信息:
但是丟失的42-45如何處理呢?在接收到第3段(37-41)之前,接收設備不會發送確認信息,也不會發送這一段之后字節的確認信息。發送設備可以將新的字節添加到第3類之后,即52-56。發送設備之后會停止發送,窗口停留在37-41。
TCP包括一個傳輸及重傳的計時機制。TCP會重傳丟失的片段。但有一個缺陷是:因為它不會對每一個片段分別進行確認,這可能會導致其他實際上已經接收到的片段被重傳(比如42至45)。
二、流量控制
流量控制方面主要有兩個要點需要掌握。一是TCP利用滑動窗口實現流量控制的機制;二是如何考慮流量控制中的傳輸效率。
1. 流量控制
所謂流量控制,主要是接收方傳遞信息給發送方,使其不要發送數據太快,是一種端到端的控制。主要的方式就是返回的ACK中會包含自己的接收窗口的大小,並且利用大小來控制發送方的數據發送:
這里面涉及到一種情況,如果B已經告訴A自己的緩沖區已滿,於是A停止發送數據;等待一段時間后,B的緩沖區出現了富余,於是給A發送報文告訴A我的rwnd大小為400,但是這個報文不幸丟失了,於是就出現A等待B的通知||B等待A發送數據的死鎖狀態。為了處理這種問題,TCP引入了持續計時器(Persistence timer),當A收到對方的零窗口通知時,就啟用該計時器,時間到則發送一個1字節的探測報文,對方會在此時回應自身的接收窗口大小,如果結果仍未0,則重設持續計時器,繼續等待。
2. 傳遞效率
一個顯而易見的問題是:單個發送字節單個確認,和窗口有一個空余即通知發送方發送一個字節,無疑增加了網絡中的許多不必要的報文(請想想為了一個字節數據而添加的40字節頭部吧!),所以我們的原則是盡可能一次多發送幾個字節,或者窗口空余較多的時候通知發送方一次發送多個字節。對於前者我們廣泛使用Nagle算法,即:
*1. 若發送應用進程要把發送的數據逐個字節地送到TCP的發送緩存,則發送方就把第一個數據字節先發送出去,把后面的字節先緩存起來;
*2. 當發送方收到第一個字節的確認后(也得到了網絡情況和對方的接收窗口大小),再把緩沖區的剩余字節組成合適大小的報文發送出去;
*3. 當到達的數據已達到發送窗口大小的一半或以達到報文段的最大長度時,就立即發送一個報文段;
對於后者我們往往的做法是讓接收方等待一段時間,或者接收方獲得足夠的空間容納一個報文段或者等到接受緩存有一半空閑的時候,再通知發送方發送數據。
三、擁塞控制
網絡中的鏈路容量和交換結點中的緩存和處理機都有着工作的極限,當網絡的需求超過它們的工作極限時,就出現了擁塞。擁塞控制就是防止過多的數據注入到網絡中,這樣可以使網絡中的路由器或鏈路不致過載。常用的方法就是:
1. 慢開始、擁塞控制
2. 快重傳、快恢復
一切的基礎還是慢開始,這種方法的思路是這樣的:
-1. 發送方維持一個叫做“擁塞窗口”的變量,該變量和接收端口共同決定了發送者的發送窗口;
-2. 當主機開始發送數據時,避免一下子將大量字節注入到網絡,造成或者增加擁塞,選擇發送一個1字節的試探報文;
-3. 當收到第一個字節的數據的確認后,就發送2個字節的報文;
-4. 若再次收到2個字節的確認,則發送4個字節,依次遞增2的指數級;
-5. 最后會達到一個提前預設的“慢開始門限”,比如24,即一次發送了24個分組,此時遵循下面的條件判定:
*1. cwnd < ssthresh, 繼續使用慢開始算法;
*2. cwnd > ssthresh,停止使用慢開始算法,改用擁塞避免算法;
*3. cwnd = ssthresh,既可以使用慢開始算法,也可以使用擁塞避免算法;
-6. 所謂擁塞避免算法就是:每經過一個往返時間RTT就把發送方的擁塞窗口+1,即讓擁塞窗口緩慢地增大,按照線性規律增長;
-7. 當出現網絡擁塞,比如丟包時,將慢開始門限設為原先的一半,然后將cwnd設為1,執行慢開始算法(較低的起點,指數級增長);
上述方法的目的是在擁塞發生時循序減少主機發送到網絡中的分組數,使得發生擁塞的路由器有足夠的時間把隊列中積壓的分組處理完畢。慢開始和擁塞控制算法常常作為一個整體使用,而快重傳和快恢復則是為了減少因為擁塞導致的數據包丟失帶來的重傳時間,從而避免傳遞無用的數據到網絡。快重傳的機制是:
-1. 接收方建立這樣的機制,如果一個包丟失,則對后續的包繼續發送針對該包的重傳請求;
-2. 一旦發送方接收到三個一樣的確認,就知道該包之后出現了錯誤,立刻重傳該包;
-3. 此時發送方開始執行“快恢復”算法:
*1. 慢開始門限減半;
*2. cwnd設為慢開始門限減半后的數值;
*3. 執行擁塞避免算法(高起點,線性增長);