1. 程式人生 > >TCP/IP詳解--TCP/IP可靠的原理 滑動視窗 擁塞視窗

TCP/IP詳解--TCP/IP可靠的原理 滑動視窗 擁塞視窗

TCP和UDP處在同一層---運輸層,但是TCP和UDP最不同的地方是,TCP提供了一種可靠的資料傳輸服務,TCP是面向連線的,也就是說,利用TCP通訊的兩臺主機首先要經歷一個“撥打電話”的過程,等到通訊準備結束才開始傳輸資料,最後結束通話。所以TCP要比UDP可靠的多,UDP是把資料直接發出去,而不管對方是不是在收信,就算是UDP無法送達,也不會產生ICMP差錯報文,這一經時重申了很多遍了。

把TCP保證可靠性的簡單工作原理摘抄如下

  • 應用資料被分割成TCP認為最適合傳送的資料塊。這和UDP完全不同,應用程式產生的 資料報長度將保持不變。由TCP傳遞給IP的資訊單位稱為報文段或段( segment)(參見圖1 - 7)。在1 8.4節我們將看到TCP如何確定報文段的長度。
  • 當TCP發出一個段後,它啟動一個定時器,等待目的端確認收到這個報文段。如果不能 及時收到一個確認,將重發這個報文段。在第21章我們將瞭解TCP協議中自適應的超時 及重傳策略。
  • 當TCP收到發自TCP連線另一端的資料,它將傳送一個確認。這個確認不是立即傳送,通常將推遲幾分之一秒,這將在1 9.3節討論。
  • TCP將保持它首部和資料的檢驗和。這是一個端到端的檢驗和,目的是檢測資料在傳輸 過程中的任何變化。如果收到段的檢驗和有差錯, T P將丟棄這個報文段和不確認收到此報文段(希望發端超時並重發)。
  • 既然TCP報文段作為IP資料報來傳輸,而IP資料報的到達可能會失序,因此TCP報文段 的到達也可能會失序。如果必要, TCP將對收到的資料進行重新排序,將收到的資料以正確的順序交給應用層。
  • TCP還能提供流量控制。TCP連線的每一方都有固定大小的緩衝空間。TCP的接收端只允許另一端傳送接收端緩衝區所能接納的資料。這將防止較快主機致使較慢主機的緩衝區溢位。

從這段話中可以看到,TCP中保持可靠性的方式就是超時重發,這是有道理的,雖然TCP也可以用各種各樣的ICMP報文來處理這些,但是這也不是可靠的,最可靠的方式就是隻要不得到確認,就重新發送資料報,直到得到對方的確認為止。

TCP的首部和UDP首部一樣,都有傳送埠號和接收埠號。但是顯然,TCP的首部資訊要比UDP的多,可以看到,TCP協議提供了傳送和確認所需要的所有必要的資訊。這在P171-173有詳細地介紹。可以想象一個TCP資料的傳送應該是如下的一個過程。

  • 雙方建立連線
  • 傳送方給接受方TCP資料報,然後等待對方的確認TCP資料報,如果沒有,就重新發,如果有,就傳送下一個資料報。
  • 接受方等待發送方的資料報,如果得到資料報並檢驗無誤,就傳送ACK(確認)資料報,並等待下一個TCP資料報的到來。直到接收到FIN(傳送完成資料報)
  • 中止連線

可以想見,為了建立一個TCP連線,系統可能會建立一個新的程序(最差也是一個執行緒),來進行資料的傳送

TCP是一個面向連線的協議,所以在連線雙方傳送資料之前,都需要首先建立一條連線。這和前面講到的協議完全不同。前面講的所有協議都只是傳送資料而已,大多數都不關心傳送的資料是不是送到,UDP尤其明顯,從程式設計的角度來說,UDP程式設計也要簡單的多----UDP都不用考慮資料分片。

TCP的資料流大致可以分為兩類,互動資料流與成塊的資料流。互動資料流就是傳送控制命令的資料流,比如relogintelnetftp命令等等;成塊資料流是用來發送資料的包,網路上大部分的TCP包都是這種包。

很明顯,TCP在傳輸這兩種型別的包時的效率是不一樣的,因此為了提高TCP的傳輸效率,應該對這兩種型別的包採用不同的演算法。

總之,TCP的傳輸原則是儘量減少小分組傳輸的數量。

TCP的互動式資料流

?經受時延的確認技術

TCP的互動式資料流通常使用“經過時延的確認”技術。通常Server在接收到從Client傳送過來的資料時,並不馬上傳送ACK,而是等一小段時間,看看本機是否有資料要反饋給Client,如果有,就將資料包含在此ACK包中,以前傳送給Client。一般情況下這個時延為200ms。需要注意的時這個200ms的定時器時相對於核心的時鐘滴答的,也就是jeffs的。加入一個數據分組到達後,此定時器已經pass100ms,那麼再過100ms ACK才會被髮送,如果在這100ms內有資料要反饋,則在100msACK會和資料一起傳送。

?Nagle演算法分析。

Nagle演算法主要用來預防小分組的產生。在廣域網上,大量TCP小分組極有可能造成網路的擁塞。

       Nagle時針對每一個TCP連線的。它要求一個TCP連線上最多隻能有一個未被確認的小分組。在改分組的確認到達之前不能傳送其他小分組。TCP會蒐集這些小的分組,然後在之前小分組的確認到達後將剛才蒐集的小分組合併發送出去。

有時候我們必須要關閉Nagle演算法,特別是在一些對時延要求較高的互動式操作環境中,所有的小分組必須儘快傳送出去。

我們可以通過程式設計取消Nagle演算法,利用TCP_NODELAY選項來關閉Nagle演算法。

TCP成塊資料流

TCP成塊資料流相關的東西有很多,比如流量控制,緊急資料傳輸,資料視窗大小調整等等。

?正常資料流

TCP通常不會對每個到達的資料分段進行確認操作,通常一個ACK報文可以確認多個成塊資料段報文,通常情況下是兩個成塊資料報文段需要一個ACK報文確認。通常是由下面的原有造成的:當收到一個報文後,此TCP連線被標識未一個未完成的時延確認,當再次收到一個數據報文後,此連線有兩個未確認的報文段,TCP馬上傳送一個ACK,當第三個資料報文到達後,第四個報文到達前,通常此TCP連線已經經過了200ms延時,因此一個ACK被髮送,這樣的迴圈周而復始,從而出現了一個ACK確認兩個資料報文的情況。當然,ACK的產生很大程度上和其接收資料報文段的時間緊密相關,也就是和Client段傳送資料的頻率相關,和網路擁塞程度相關,和ClientServer兩端的處理能力相關,總是是一個多因素決定的結果。

?TCP的滑動視窗協議

TCP使用滑動視窗協議來進行流量控制。特別需要注意的是,滑動視窗是一個抽象的概念,它是針對每一個TCP連線的,而且是有方向的,一個TCP連線應該有兩個滑動視窗,每個資料傳輸方向上有一個,而不是針對連線的每一端的。

視窗左邊沿向右邊滑動叫做視窗合攏,表示傳送方傳送了資料或者接收到了確認;視窗右邊沿向右邊滑動叫做視窗的張開,表示資料已經被使用者空間程序接收並且釋放了快取;視窗左邊沿向左移動則表明此ACK是重複ACK,應該丟棄;視窗右邊沿向左移動叫做視窗收縮,一般不會有人這樣做。

當左邊沿和右邊沿重合的時候表明視窗大小是0,此時傳送方不應該在傳送資料了,因為接收方的接收緩衝區已滿,使用者程序還沒以接收。當用戶程序接收完成後,接收方應該傳送一個ACK,表明此時的接收視窗已經恢復,此ACK的序號同前一個win0ACK相同。

同樣,在實現中,傳送方不必傳送一個全視窗的資料,但是它當然可以這樣做。ACK總是將視窗向右邊滑動,視窗的大小可以減小,接收方在傳送ACK之前不必等待視窗被填滿(即變為0),很多實現是收到兩個資料報文段後立刻傳送ACK

?TCP視窗大小的調整

TCP視窗的大小通常由接收端來確認,也就是在TCP建立連線的第二個SYN+ACK報文的Win欄位來確認。

當然,程式可以隨時改變這個視窗(快取)的大小。預設的視窗大小是4096位元組,但是對於檔案傳輸來說這並不是一個理想的數字,如果程式的主要目的是傳輸檔案,那麼最好將這個快取設定到最大,但是這樣可能會造成傳送端連續傳送多個數據報文段後,接收方才反饋一個ACK的情況,當然,這也沒有什麼不可以的,只要不超時,就不算錯。

?TCPPUSH

PUSHTCP報頭中的一個標誌位,傳送方在傳送資料的時候可以設定這個標誌位。該標誌通知接收方將接收到的資料全部提交給接收程序。這裡所說的資料包括與此PUSH包一起傳輸的資料以及之前就為該程序傳輸過來的資料。

Server端收到這些資料後,它需要立刻將這些資料提交給應用層程序,而不再等待是否還有額外的資料到達。

那麼應該合適設定PUSH標誌呢?實際上現在的TCP協議棧基本上都可以自行處理這個問題,而不是交給應用層處理。如果待發送的資料會清空傳送快取,那麼棧就會自動為此包設定PUSH標誌,源於BSD的棧一般都會這麼做,而且,BSD TCP STACK也從來不會將收到的資料推遲提交給應用程式,因此,在BSD TCP STACK中,PUSH位是被忽略的,因為根本就沒有用。

?TCP的慢啟動(擁塞視窗)

TCP在區域網環境中的效率是很高的,但是到了廣域網的環境中情況就不同了,在傳送方和接收方之間可能存在多個Router以及一些速率比較慢的鏈路,而且一些中繼路由器必須快取分組,還可能分片,所以在廣域網的環境中,TCP的效率可能出現問題。

為了解決這個問題,現在的TCP棧都支援“慢啟動”演算法,即擁塞視窗控制演算法。該演算法通過觀察到新分組進入網路的速率與另一端返回ACK的速率相同而工作。其實,擁塞視窗是傳送方使用的一種流量控制演算法。

慢啟動為TCP的傳送方增加了一個擁塞視窗,當連線建立時,擁塞視窗被初始化為一個報文段大小,每收到一個ACK,擁塞視窗就會增加一個報文段,傳送方取擁塞視窗與通過視窗的最小值作為傳送的上限。

?TCP成塊資料吞吐量

TCP視窗大小,視窗流量控制,慢啟動對TCP的成塊資料傳輸綜合作用,可能對TCP的資料傳輸有意想不到的影響。

       RTTRound-Trip Time:往返時間。是指一個報文段從發出去到收到此報文段的ACK所經歷的時間。通常一個報文段的RTT與傳播時延和傳送時延兩個因素相關。

在傳送的過程中有可能發生這樣的情況,即TCP兩端的傳輸“管道”被填滿,即整個管道上都有資料在跑,此時不管擁塞視窗和通告視窗是多少,管道上都不能在容納更多的資料了。此時每當接收方從網路上移去一個報文段,傳送方就傳送一個,但是管道上的ACK總是固定的,這種情況就是連線的理想穩定狀態。

一般情況下頻寬*時延就是一條線路的容量,因此吧RTT減小可以增加一條線路的容量,注意RTT加大的意思時傳輸時間減小!

當 資料由一個大的管道向一個小的管道傳輸時,就有可能發生擁塞,例如,當若干輸入流到達一個路由器,而此路由器的輸出頻寬小於這些輸入流的頻寬總和時,就會 發生擁塞。這種情況普遍見於區域網與廣域網的介面處。如果傳送方處於區域網,而且不使用慢啟動,使用區域網的頻寬儘快的傳送報文,那麼返回的ACK之間的間隔與最慢的廣域網鏈路一致。而且,由於路由器轉發包速度慢,所以路由器就有可能主動丟失分組包。

?TCP的緊急方式

TCP提供了一種“緊急方式”的資料傳輸方式,TCP的一端可以告訴另一端有些具有某種方式的緊急資料被放在了普通的資料流中,接收方可以自行選擇處理。緊急方式客廳通過設定TCPURG標識位與緊急指標的偏移量來設定。這個緊急指標指向緊急資料的最後一個位元組(也有可能是最後一個位元組的下一個位元組)。

現在有許多實現將緊急方式叫做“帶外資料”,其實這是不正確的。

目前緊急指標被用來禁止停止FTP的資料傳輸。不過總的來說,用的不多。

對於資料傳輸來說,如果用緊急資料來傳輸大量資料,這種方法顯然是不可取的,再建立一個TCP連線不是更簡單有效嗎?

====================================================================

http://zhidao.baidu.com/question/98620785

為了防止網路的擁塞現象,TCP提出了一系列的擁塞控制機制。最初由V. Jacobson在1988年的論文中提出的TCP的擁塞控制由“慢啟動(Slow start)”和“擁塞避免(Congestion avoidance)”組成,後來TCP Reno版本中又針對性的加入了“快速重傳(Fast retransmit)”、“快速恢復(Fast Recovery)”演算法,再後來在TCP NewReno中又對“快速恢復”演算法進行了改進,近些年又出現了選擇性應答( selective acknowledgement,SACK)演算法,還有其他方面的大大小小的改進,成為網路研究的一個熱點。

TCP的擁塞控制主要原理依賴於一個擁塞視窗(cwnd)來控制,在之前我們還討論過TCP還有一個對端通告的接收視窗(rwnd)用於流量控制。視窗值的大小就代表能夠傳送出去的但還沒有收到ACK的最大資料報文段,顯然視窗越大那麼資料傳送的速度也就越快,但是也有越可能使得網路出現擁塞,如果視窗值為1,那麼就簡化為一個停等協議,每傳送一個數據,都要等到對方的確認才能傳送第二個資料包,顯然資料傳輸效率低下。TCP的擁塞控制演算法就是要在這兩者之間權衡,選取最好的cwnd值,從而使得網路吞吐量最大化且不產生擁塞。

由於需要考慮擁塞控制和流量控制兩個方面的內容,因此TCP的真正的傳送視窗=min(rwnd, cwnd)。但是rwnd是由對端確定的,網路環境對其沒有影響,所以在考慮擁塞的時候我們一般不考慮rwnd的值,我們暫時只討論如何確定cwnd值的大小。關於cwnd的單位,在TCP中是以位元組來做單位的,我們假設TCP每次傳輸都是按照MSS大小來發送資料的,因此你可以認為cwnd按照資料包個數來做單位也可以理解,所以有時我們說cwnd增加1也就是相當於位元組數增加1個MSS大小。

慢啟動:最初的TCP在連線建立成功後會向網路中傳送大量的資料包,這樣很容易導致網路中路由器快取空間耗盡,從而發生擁塞。因此新建立的連線不能夠一開始就大量傳送資料包,而只能根據網路情況逐步增加每次傳送的資料量,以避免上述現象的發生。具體來說,當新建連線時,cwnd初始化為1個最大報文段(MSS)大小,傳送端開始按照擁塞視窗大小發送資料,每當有一個報文段被確認,cwnd就增加1個MSS大小。這樣cwnd的值就隨著網路往返時間(Round Trip Time,RTT)呈指數級增長,事實上,慢啟動的速度一點也不慢,只是它的起點比較低一點而已。我們可以簡單計算下:

   開始           --->     cwnd = 1

   經過1個RTT後   --->     cwnd = 2*1 = 2

   經過2個RTT後   --->     cwnd = 2*2= 4

   經過3個RTT後   --->     cwnd = 4*2 = 8

如果頻寬為W,那麼經過RTT*log2W時間就可以佔滿頻寬。

擁塞避免:從慢啟動可以看到,cwnd可以很快的增長上來,從而最大程度利用網路頻寬資源,但是cwnd不能一直這樣無限增長下去,一定需要某個限制。TCP使用了一個叫慢啟動門限(ssthresh)的變數,當cwnd超過該值後,慢啟動過程結束,進入擁塞避免階段。對於大多數TCP實現來說,ssthresh的值是65536(同樣以位元組計算)。擁塞避免的主要思想是加法增大,也就是cwnd的值不再指數級往上升,開始加法增加。此時當視窗中所有的報文段都被確認時,cwnd的大小加1,cwnd的值就隨著RTT開始線性增加,這樣就可以避免增長過快導致網路擁塞,慢慢的增加調整到網路的最佳值。

上面討論的兩個機制都是沒有檢測到擁塞的情況下的行為,那麼當發現擁塞了cwnd又該怎樣去調整呢?

首先來看TCP是如何確定網路進入了擁塞狀態的,TCP認為網路擁塞的主要依據是它重傳了一個報文段。上面提到過,TCP對每一個報文段都有一個定時器,稱為重傳定時器(RTO),當RTO超時且還沒有得到資料確認,那麼TCP就會對該報文段進行重傳,當發生超時時,那麼出現擁塞的可能性就很大,某個報文段可能在網路中某處丟失,並且後續的報文段也沒有了訊息,在這種情況下,TCP反應比較“強烈”:

1.把ssthresh降低為cwnd值的一半

2.把cwnd重新設定為1

3.重新進入慢啟動過程。

從整體上來講,TCP擁塞控制視窗變化的原則是AIMD原則,即加法增大、乘法減小。可以看出TCP的該原則可以較好地保證流之間的公平性,因為一旦出現丟包,那麼立即減半退避,可以給其他新建的流留有足夠的空間,從而保證整個的公平性。

其實TCP還有一種情況會進行重傳:那就是收到3個相同的ACK。TCP在收到亂序到達包時就會立即傳送ACK,TCP利用3個相同的ACK來判定資料包的丟失,此時進行快速重傳,快速重傳做的事情有:

1.把ssthresh設定為cwnd的一半

2.把cwnd再設定為ssthresh的值(具體實現有些為ssthresh+3)

3.重新進入擁塞避免階段。

後來的“快速恢復”演算法是在上述的“快速重傳”演算法後新增的,當收到3個重複ACK時,TCP最後進入的不是擁塞避免階段,而是快速恢復階段。快速重傳和快速恢復演算法一般同時使用。快速恢復的思想是“資料包守恆”原則,即同一個時刻在網路中的資料包數量是恆定的,只有當“老”資料包離開了網路後,才能向網路中傳送一個“新”的資料包,如果傳送方收到一個重複的ACK,那麼根據TCP的ACK機制就表明有一個數據包離開了網路,於是cwnd加1。如果能夠嚴格按照該原則那麼網路中很少會發生擁塞,事實上擁塞控制的目的也就在修正違反該原則的地方。

具體來說快速恢復的主要步驟是:

1.當收到3個重複ACK時,把ssthresh設定為cwnd的一半,把cwnd設定為ssthresh的值加3,然後重傳丟失的報文段,加3的原因是因為收到3個重複的ACK,表明有3個“老”的資料包離開了網路。 

2.再收到重複的ACK時,擁塞視窗增加1。

3.當收到新的資料包的ACK時,把cwnd設定為第一步中的ssthresh的值。原因是因為該ACK確認了新的資料,說明從重複ACK時的資料都已收到,該恢復過程已經結束,可以回到恢復之前的狀態了,也即再次進入擁塞避免狀態。

快速重傳演算法首次出現在4.3BSD的Tahoe版本,快速恢復首次出現在4.3BSD的Reno版本,也稱之為Reno版的TCP擁塞控制演算法。

可以看出Reno的快速重傳演算法是針對一個包的重傳情況的,然而在實際中,一個重傳超時可能導致許多的資料包的重傳,因此當多個數據包從一個數據視窗中丟失時並且觸發快速重傳和快速恢復演算法時,問題就產生了。因此NewReno出現了,它在Reno快速恢復的基礎上稍加了修改,可以恢復一個視窗內多個包丟失的情況。具體來講就是:Reno在收到一個新的資料的ACK時就退出了快速恢復狀態了,而NewReno需要收到該視窗內所有資料包的確認後才會退出快速恢復狀態,從而更一步提高吞吐量。

SACK就是改變TCP的確認機制,最初的TCP只確認當前已連續收到的資料,SACK則把亂序等資訊會全部告訴對方,


由接收方提供的視窗的大小通常可以由接收程序控制,這將影響TCP的效能。sock API雨蕁程序設定傳送和接收快取的大小。接收快取的大小是該連線上所能夠通告的最大視窗大小。傳送發一開始便向網路傳送多個報文段,直至的視窗達到接收方通告大小為止。當傳送發和接收方處於同一個區域網時,這種方式是可以的。但是如果在傳送方和接收方之間存在多個路由器和速率較慢的鏈路時,就有可能出現一些問題。一些中間路由器必須快取分組,並有可能耗盡儲存器的空間。TCP需要支援一種被稱為慢啟動的演算法。該演算法通過觀察到新分組進入網路的速率應該與另一端返回確認的速率相同而進行工作。慢啟動為傳送方的TCP增加了另一個視窗:擁塞視窗,cwnd。當與另一個網路的主機建立TCP連線時,擁塞視窗被初始化為1個報文段,即另一端通過的報文段大小。每收到一個ACK,擁塞視窗就增加一個報文段,cwnd以位元組為單位,但是慢啟動以報文段大小為單位進行增加。傳送方取擁塞視窗與通告視窗中的最小值作為傳送上限、擁塞視窗是傳送方使用的流量控制,而通告視窗則是接收方使用的流量控制。傳送方開始時傳送一個報文段,然後等待ACK。當接收該ACK時,擁塞視窗從1增加為2,即可以傳送2個報文段。當收到這2個報文段的ACK時,擁塞視窗就增加為4。這是一種指數增加的關係。