1.端到端的TCP協(xié)議和IP協(xié)議之間的矛盾
端到端的TCP只能看到兩個(gè)節(jié)點(diǎn),那就是自己和對(duì)方,它們是看不到任何中間的路徑的。可是IP網(wǎng)絡(luò)卻是一跳一跳的,它們的矛盾之處在于TCP的端到端流量控制必然會(huì)導(dǎo)致網(wǎng)絡(luò)擁堵。因?yàn)槊織lTCP連接的一端只知道它對(duì)端還有多少空間用于接收數(shù)據(jù),它們并不管到達(dá)對(duì)端的路徑上是否還有這么大的容量,事實(shí)上所有連接的這些空間加在一起將瞬間超過IP網(wǎng)絡(luò)的容量,因此TCP也不可能按照滑動(dòng)窗口流量控制機(jī)制很理想的運(yùn)行。
勢(shì)必需要一種擁塞控制機(jī)制,反應(yīng)路徑的擁塞情況。
疑難雜癥15:擁塞控制的本質(zhì)
由于TCP是端到端協(xié)議,因此兩端之間的控制范疇屬于流量控制,IP網(wǎng)絡(luò)的擁塞會(huì)導(dǎo)致TCP分段的丟失,由于TCP看不到中間的路由器,因此這種丟失只會(huì)發(fā)生中間路由器,當(dāng)然兩個(gè)端點(diǎn)的網(wǎng)卡或者IP層丟掉數(shù)據(jù)分段也是TCP看不到的。因此擁塞控制必然作用于IP鏈路。事實(shí)上我們可以得知,只有在以下情況下?lián)砣刂撇艜?huì)起作用:
a.兩個(gè)或兩個(gè)以上的連接(其中一個(gè)一定要是TCP,另一個(gè)可以是任意連接)經(jīng)過同一個(gè)路由器或者同一個(gè)鏈路時(shí);
b.只有一個(gè)TCP連接,然而它經(jīng)過了一個(gè)路由器時(shí)。
其它情況下是不會(huì)擁塞的。因?yàn)橐粋(gè)TCP總是希望獨(dú)享整條網(wǎng)絡(luò)通路,而這對(duì)于多個(gè)連接而言是不可能的,必須保證TCP的公平性,這樣這種擁塞控制機(jī)制才合理。本質(zhì)上,擁塞的原因就是大家都想獨(dú)享全部帶寬資源,結(jié)果導(dǎo)致?lián)砣@也是合理的,畢竟TCP看不到網(wǎng)絡(luò)的狀態(tài),同時(shí)這也決定了TCP的擁塞控制必須采用試探性的方式,最終到達(dá)一個(gè)足以引起其“反應(yīng)”的“刺激點(diǎn)”。
擁塞控制需要完成以下兩個(gè)任務(wù):1.公平性;2.擁塞之后退出擁塞狀態(tài)。
疑難雜癥16:影響擁塞的因素
我們必須認(rèn)識(shí)到擁塞控制是一個(gè)整體的機(jī)制,它不偏向于任何TCP連接,因此這個(gè)機(jī)制內(nèi)在的就包含了公平性。那么影響擁塞的因素都有什么呢?具有諷刺意味的是,起初TCP并沒有擁塞控制機(jī)制,正是TCP的超時(shí)重傳風(fēng)暴(一個(gè)分段丟失造成后續(xù)的已經(jīng)發(fā)送的分段均被重傳,而這些重傳大多數(shù)是不必要的)加重了網(wǎng)絡(luò)的擁塞。因此重傳必然不能過頻,必須把重傳定時(shí)器的超時(shí)時(shí)間設(shè)置的稍微長(zhǎng)一些,而這一點(diǎn)在單一重傳定時(shí)器的設(shè)計(jì)中得到了加強(qiáng)。除此TCP自身的因素之外,其它所有的擁塞都可以靠擁塞控制機(jī)制來(lái)自動(dòng)完成。
另外,不要把路由器想成一種線速轉(zhuǎn)發(fā)設(shè)備,再好的路由器只要接入網(wǎng)絡(luò),總是會(huì)拉低網(wǎng)絡(luò)的總帶寬,因此即使只有一個(gè)TCP連接,由于TCP的發(fā)送方總是以發(fā)送鏈路的帶寬發(fā)送分段,這些分段在經(jīng)過路由器的時(shí)候排隊(duì)和處理總是會(huì)有時(shí)延,因此最終肯定會(huì)丟包的。
最后,丟包的延后性也會(huì)加重?fù)砣。假設(shè)一個(gè)TCP連接經(jīng)過了N個(gè)路由器,前N-1個(gè)路由器都能順利轉(zhuǎn)發(fā)TCP分段,但是最后一個(gè)路由器丟失了一個(gè)分段,這就導(dǎo)致了這些丟失的分段浪費(fèi)了前面路由器的大量帶寬。
2.擁塞控制的策略
在介紹擁塞控制之前,首先介紹一下?lián)砣翱,它?shí)際上表示的也是“可以發(fā)送多少數(shù)據(jù)”,然而這個(gè)和接收端通告的接收窗口意義是不一樣的,后者是流量控制用的窗口,而前者是擁塞控制用的窗口,體現(xiàn)了網(wǎng)絡(luò)擁塞程度。
擁塞控制整體上分為兩類,一類是試探性的擁塞探測(cè),另一類則是擁塞避免(注意,不是常規(guī)意義上的擁塞避免)。
2.1.試探性的擁塞探測(cè)分為兩類,之一是慢啟動(dòng),之二是擁塞窗口加性擴(kuò)大(也就是熟知的擁塞避免,然而這種方式是避免不了擁塞的)。
2.2.擁塞避免方式擁塞控制旨在還沒有發(fā)生擁塞的時(shí)候就先提醒發(fā)送端,網(wǎng)絡(luò)擁塞了,這樣發(fā)送端就要么可以進(jìn)入快速重傳/快速恢復(fù)或者顯式的減小擁塞窗口,這樣就避免網(wǎng)絡(luò)擁塞的一沓糊涂之后出現(xiàn)超時(shí),從而進(jìn)入慢啟動(dòng)階段。
2.3.快速重傳和快速恢復(fù)。所謂快速重傳/快速恢復(fù)是針對(duì)慢啟動(dòng)的,我們知道慢啟動(dòng)要從1個(gè)MSS開始增加擁塞窗口,而快速重傳/快速恢復(fù)則是一旦收到3個(gè)冗余ACK,不必進(jìn)入慢啟動(dòng),而是將擁塞窗口縮小為當(dāng)前閥值的一半加上3,然后如果繼續(xù)收到冗余ACK,則將擁塞窗口加1個(gè)MSS,直到收到一個(gè)新的數(shù)據(jù)ACK,將窗口設(shè)置成正常的閥值,開始加性增加的階段。
當(dāng)進(jìn)入快速重傳時(shí),為何要將擁塞窗口縮小為當(dāng)前閥值的一半加上3呢?加上3是基于數(shù)據(jù)包守恒來(lái)說的,既然已經(jīng)收到了3個(gè)冗余ACK,說明有三個(gè)數(shù)據(jù)分段已經(jīng)到達(dá)了接收端,既然三個(gè)分段已經(jīng)離開了網(wǎng)絡(luò),那么就是說可以在發(fā)送3個(gè)分段了,只要再收到一個(gè)冗余ACK,這也說明1個(gè)分段已經(jīng)離開了網(wǎng)絡(luò),因此就將擁塞窗口加1個(gè)MSS。直到收到新的ACK,說明直到收到第三個(gè)冗余ACK時(shí)期發(fā)送的TCP分段都已經(jīng)到達(dá)對(duì)端了,此時(shí)進(jìn)入正常階段開始加性增加擁塞窗口。
疑難雜癥17:超時(shí)重傳和收到3個(gè)冗余ACK后重傳
這兩種重傳的意義是不同的,超時(shí)重傳一般是因?yàn)榫W(wǎng)絡(luò)出現(xiàn)了嚴(yán)重?fù)砣?沒有一個(gè)分段到達(dá),如果有的話,肯定會(huì)有ACK的,若是正常ACK,則重置重傳定時(shí)器,若是冗余ACK,則可能是個(gè)別報(bào)文丟失或者被重排序,若連續(xù)3個(gè)冗余ACK,則很有可能是個(gè)別分段丟失),此時(shí)需要更加嚴(yán)厲的縮小擁塞窗口,因此此時(shí)進(jìn)入慢啟動(dòng)階段。而收到3個(gè)冗余ACK后說明確實(shí)有中間的分段丟失,然而后面的分段確實(shí)到達(dá)了接收端,這因?yàn)檫@樣才會(huì)發(fā)送冗余ACK,這一般是路由器故障或者輕度擁塞或者其它不太嚴(yán)重的原因引起的,因此此時(shí)擁塞窗口縮小的幅度就不能太大,此時(shí)進(jìn)入快速重傳/快速恢復(fù)階段。
疑難雜癥18:為何收到3個(gè)冗余ACK后才重傳
這是一種權(quán)衡的結(jié)構(gòu),收到兩個(gè)或者一個(gè)冗余ACK也可以重傳,但是這樣的話可能或造成不必要的重傳,因?yàn)閮蓚(gè)數(shù)據(jù)分段發(fā)生亂序的可能性不大,超過三個(gè)分段發(fā)生亂序的可能性才大,換句話說,如果僅僅收到一個(gè)亂序的分段,那很可能被中間路由器重排了,那么另一個(gè)分段很可能馬上就到,然而如果連續(xù)收到了3個(gè)分段都沒能彌補(bǔ)那個(gè)缺漏,那很可能是它丟失了,需要重傳。因此3個(gè)冗余ACK是一種權(quán)衡,在減少不必要重傳和確實(shí)能檢測(cè)出單個(gè)分段丟失之間所作的權(quán)衡。
注意,冗余ACK是不能捎帶的。
疑難雜癥19:乘性減和加性增的深層含義
為什么是乘性減而加性增呢?擁塞窗口的增加受惠的只是自己,而擁塞窗口減少受益的大家,可是自己卻受到了傷害。哪一點(diǎn)更重要呢?我們知道TCP的擁塞控制中內(nèi)置了公平性,恰恰就是這種乘性減實(shí)現(xiàn)了公平性。擁塞窗口的1個(gè)MSS的改變影響一個(gè)TCP發(fā)送者,為了使得自己擁塞窗口的減少影響更多的TCP發(fā)送者- 讓更多的發(fā)送者受益,那么采取了乘性減的策略。
當(dāng)然,BIC算法提高了加性增的效率,不再一個(gè)一個(gè)MSS的加,而是一次加比較多的MSS,采取二分查找的方式逐步找到不丟包的點(diǎn),然后加性增。
疑難雜癥20:TCP連接的傳輸穩(wěn)定狀態(tài)是什么
首先,先說一下發(fā)送端的發(fā)送窗口怎么確定,它取的是擁塞窗口和接收端通告窗口的最小值。然后,我們提出三種發(fā)送窗口的穩(wěn)定狀態(tài):
a.IP互聯(lián)網(wǎng)絡(luò)上接收端擁有大窗口的經(jīng)典鋸齒狀
b.IP互聯(lián)網(wǎng)絡(luò)上接收端擁有小窗口的直線狀態(tài)
c.直連網(wǎng)絡(luò)端點(diǎn)間的滿載狀態(tài)下的直線狀態(tài)
其中a是大多數(shù)的狀態(tài),因?yàn)橐话愣,TCP連接都是建立在互聯(lián)網(wǎng)上的,而且是大量的,比如Web瀏覽,電子郵件,網(wǎng)絡(luò)游戲,F(xiàn)tp下載等等。TCP發(fā)送端用慢啟動(dòng)或者擁塞避免方式不斷增加其擁塞窗口,直到丟包的發(fā)生,然后進(jìn)入慢啟動(dòng)或者擁塞避免階段(要看是由于超時(shí)丟包還是由于冗余ACK丟包),此時(shí)發(fā)送窗口將下降到1或者下降一半,這種情況下,一般接收端的接收窗口是比較大的,畢竟IP網(wǎng)絡(luò)并不是什么很快速的網(wǎng)絡(luò),一般的機(jī)器處理速度都很快。
但是如果接收端特別破,處理速度很慢,就會(huì)導(dǎo)致其通告一個(gè)很小的窗口,這樣的話,即使擁塞窗口再大,發(fā)送端也還是以通告的接收窗口為發(fā)送窗口,這樣就不會(huì)發(fā)生擁塞。最后,如果的TCP連接運(yùn)行在一個(gè)直連的兩臺(tái)主機(jī)上,那么它將獨(dú)享網(wǎng)絡(luò)帶寬,這樣該TCP的數(shù)據(jù)流在的情況下將填滿網(wǎng)絡(luò)管道(我們把網(wǎng)絡(luò)管道定義為帶寬和延時(shí)的乘積),其實(shí)在這種情況下是不存在擁塞的,就像你一個(gè)人獨(dú)自徘徊在飄雨黃昏的街頭一樣...
2.4.主動(dòng)的擁塞避免
前面我們描述的擁塞控制方式都是試探性的檢測(cè),然后擁塞窗口被動(dòng)的進(jìn)行乘性減,這樣在接收端窗口很大的情況下(一般都是這樣,網(wǎng)絡(luò)擁堵,分段就不會(huì)輕易到達(dá)接收端,導(dǎo)致接收端的窗口大量空置)就可能出現(xiàn)鋸齒形狀的“時(shí)間-窗口”圖,類似在一個(gè)擁堵的北京X環(huán)上開車,發(fā)送機(jī)發(fā)動(dòng),車開動(dòng),停止,等待,發(fā)動(dòng)機(jī)發(fā)動(dòng),車開動(dòng)...聽聲音也能聽出來(lái)。
雖然TCP看不到下面的IP網(wǎng)絡(luò),然而它還是可以通過檢測(cè)RTT的變化以及擁塞窗口的變化推算出IP網(wǎng)絡(luò)的擁堵情況的。就比方說北京東四環(huán)一家快遞公司要持續(xù)送快遞到西四環(huán),當(dāng)發(fā)件人發(fā)現(xiàn)貨到時(shí)間越來(lái)越慢的時(shí)候,他會(huì)意識(shí)到“下班高峰期快到了”...
可以通過持續(xù)觀測(cè)RTT的方式來(lái)主動(dòng)調(diào)整擁塞窗口的大小而不是一味的加性增。然而還有更猛的算法,那就是計(jì)算兩個(gè)差值的乘積:
(當(dāng)前擁塞窗口-上一次擁塞窗口)x(當(dāng)前的RTT-上一次的RTT)
如果結(jié)果是正數(shù),則擁塞窗口減少1/8,若結(jié)果是負(fù)數(shù)或者0,則窗口增加一個(gè)MSS。注意,這回不再是乘性減了,可以看出,減的幅度比乘性減幅度小,這是因?yàn)檫@種擁塞控制是主動(dòng)的,而不是之前的那種被動(dòng)的試探方式。在試探方式中,乘性減以一種懲罰的方式實(shí)現(xiàn)了公平性,而在這里的主動(dòng)方式中,當(dāng)意識(shí)到要擁塞的時(shí)候,TCP發(fā)送者主動(dòng)的減少了擁塞窗口,為了對(duì)這種自首行為進(jìn)行鼓勵(lì),采用了小幅減少擁塞窗口的方式。需要注意的是,在擁塞窗口減小的過程中,乘積的前一個(gè)差值是負(fù)數(shù),如果后一個(gè)差值也是負(fù)數(shù),那么結(jié)果就是繼續(xù)縮減窗口,直到擁塞緩解或者窗口減少到了一定程度,使得后一個(gè)差值成了正數(shù)或者0,這種情況下,其實(shí)后一個(gè)差值只能變?yōu)?。
疑難雜癥21:路由器和TCP的互動(dòng)
雖然有了5.2.4節(jié)介紹的主動(dòng)的擁塞檢測(cè),那么路由器能不能做點(diǎn)什么幫助檢測(cè)擁塞呢?這種對(duì)路由器的擴(kuò)展是必要的,要知道,每天有無(wú)數(shù)的TCP要通過路由器,雖然路由器不管TCP協(xié)議的任何事(當(dāng)然排除連接跟蹤之類的,這里所說的是標(biāo)準(zhǔn)的IP路由器),但是它卻能以一種很簡(jiǎn)單的方式告訴TCP的兩端IP網(wǎng)絡(luò)發(fā)生了擁堵,這種方式就是當(dāng)路由器檢測(cè)到自己發(fā)生輕微擁堵的時(shí)候隨機(jī)的丟包,隨機(jī)丟包而不是連續(xù)丟包對(duì)于TCP而言是有重大意義的,隨機(jī)丟包會(huì)使TCP發(fā)現(xiàn)丟棄了個(gè)別的分段而后續(xù)的分段仍然會(huì)到達(dá)接收端,這樣TCP發(fā)送端就會(huì)接收到3個(gè)冗余ACK,然后進(jìn)入快速重傳/快速恢復(fù)而不是慢啟動(dòng)。
這就是路由器能幫TCP做的事。
其它
疑難雜癥22:如何學(xué)習(xí)TCP
很多人發(fā)帖問TCP相關(guān)的內(nèi)容,接下來(lái)稀里嘩啦的就是讓看《TCP/IP詳解》和《Unix網(wǎng)絡(luò)編程》里面的特定章節(jié),我覺得這種回答很不負(fù)責(zé)任。因?yàn)槲也⒉徽J(rèn)為這兩本書有多大的幫助,寫得確實(shí)很不錯(cuò),然而可以看出Richard Stevens是一個(gè)實(shí)用主義者,他喜歡用實(shí)例來(lái)解釋一切,《詳解》通篇都是用tcpdump的輸出來(lái)講述的,這種方式只是適合于已經(jīng)對(duì)TCP很理解的人,然而大多數(shù)的人是看不明白的。
如果想從設(shè)計(jì)的角度來(lái)說,這兩本書都很爛。我覺得應(yīng)該先看點(diǎn)入門的,比如Wiki之類的,然后看RFC文檔,793,896,1122等),這樣你就明白 TCP為何這么設(shè)計(jì)了,而這些你永遠(yuǎn)都不能在Richard Stevens的書中得到。最后,如果你想,那么就看一點(diǎn)Richard Stevens的書,最重要的還是寫點(diǎn)代碼或者敲點(diǎn)命令,然后抓包自己去分析。
疑難雜癥23:Linux,Windows和網(wǎng)絡(luò)編程
我覺得在Linux上寫點(diǎn)TCP的代碼是很不錯(cuò)的,如果有BSD那就更好了。不推薦用Winsock學(xué)習(xí)TCP。雖然微軟聲稱自己的API都是為了讓事情更簡(jiǎn)單,但實(shí)際上事情卻更復(fù)雜了,如果你用Winsock學(xué)習(xí),你就要花大量的時(shí)候去掌握一些和網(wǎng)絡(luò)編程無(wú)關(guān)但是windows平臺(tái)上卻少不了的東西
1.總結(jié)
TCP 協(xié)議是一個(gè)端到端的協(xié)議,雖然話說它是一個(gè)帶流量控制,擁塞控制的協(xié)議,然而正是因?yàn)檫@些所謂的控制才導(dǎo)致了TCP變得復(fù)雜。同時(shí)這些特性是互相雜糅的,流量控制帶來(lái)了很多問題,解決這些問題的方案最終又帶來(lái)了新的問題,這些問題在解決的時(shí)候都只考慮了端到端的意義,但實(shí)際上TCP需要盡力而為的IP提供的網(wǎng)絡(luò),因此擁塞成了最終的結(jié)癥,擁塞控制算法的改進(jìn)也成了一個(gè)單獨(dú)的領(lǐng)域。
在學(xué)習(xí)TCP的過程中,切忌一鍋粥一盤棋的方式,一定要分清楚每一個(gè)算法到底是解決什么問題的,每一個(gè)問題和其他問題到底有什么關(guān)聯(lián),這些問題的解決方案之間有什么關(guān)聯(lián),另外TCP的發(fā)展歷史也了解一下,這些都搞明白了,TCP協(xié)議就徹底被你掌控了。接下來(lái)你就可以學(xué)習(xí)Socket API了,然后高效的TCP程序出自你手!