一、TCP概念
TCP(Transmission Control Protocol 傳輸控制協(xié)議)是一種面向連接(連接導(dǎo)向)的、可靠的、 基于IP的傳輸層協(xié)議。
首先來(lái)看看OSI的七層模型
?
?
我們需要知道TCP工作在網(wǎng)絡(luò)OSI的七層模型中的第四層——傳輸層,IP在第三層——網(wǎng)絡(luò)層,ARP 在第二層——數(shù)據(jù)鏈路層;同時(shí),我們需要簡(jiǎn)單的知道,數(shù)據(jù)從
應(yīng)用層發(fā)下來(lái),會(huì)在每一層都會(huì)加上頭部信息,進(jìn)行 封裝,然后再發(fā)送到數(shù)據(jù)接收端。這個(gè)基本的流程你需要知道,就是每個(gè)數(shù)據(jù)都會(huì)經(jīng)過(guò)數(shù)據(jù)的封裝和解封 裝的過(guò)程。
在OSI七層模型中,每一層的作用和對(duì)應(yīng)的協(xié)議如下:
?
?
?
二、TCP頭部結(jié)構(gòu)和字段介紹
從上面圖片可以看出,TCP協(xié)議是封裝在IP數(shù)據(jù)包中。
?
?
?
下圖是TCP報(bào)文數(shù)據(jù)格式。TCP首部如果不計(jì)選項(xiàng)和填充字段,它通常是20個(gè)字節(jié)。
?
?
下面分別對(duì)其中的字段進(jìn)行介紹:
源端口和目的端口
各占2個(gè)字節(jié),這兩個(gè)值加上IP首部中的源端IP地址和目的端IP地址唯一確定一個(gè)TCP連接。有時(shí)一個(gè)IP地址和一個(gè)端口號(hào)也稱為socket(插口)。
序號(hào)(seq)
占4個(gè)字節(jié),是本報(bào)文段所發(fā)送的數(shù)據(jù)項(xiàng)目組第一個(gè)字節(jié)的序號(hào)。在TCP傳送的數(shù)據(jù)流中,每一個(gè)字節(jié)都有一個(gè)序號(hào)。例如,一報(bào)文段的序號(hào)為300,而且數(shù)據(jù)共100字節(jié),
則下一個(gè)報(bào)文段的序號(hào)就是400;序號(hào)是32bit的無(wú)符號(hào)數(shù),序號(hào)到達(dá)2^32-1后從0開(kāi)始。
確認(rèn)序號(hào)(ack)
占4字節(jié),是期望收到對(duì)方下次發(fā)送的數(shù)據(jù)的第一個(gè)字節(jié)的序號(hào),也就是期望收到的下一個(gè)報(bào)文段的首部中的序號(hào);確認(rèn)序號(hào)應(yīng)該是上次已成功收到數(shù)據(jù)字節(jié)序號(hào)+1。
只有ACK標(biāo)志為1時(shí),確認(rèn)序號(hào)才有效。
數(shù)據(jù)偏移
占4比特,表示數(shù)據(jù)開(kāi)始的地方離TCP段的起始處有多遠(yuǎn)。實(shí)際上就是TCP段首部的長(zhǎng)度。由于首部長(zhǎng)度不固定,因此數(shù)據(jù)偏移字段是必要的。數(shù)據(jù)偏移以32位為長(zhǎng)度單位,
也就是4個(gè)字節(jié),因此TCP首部的最大長(zhǎng)度是60個(gè)字節(jié)。即偏移最大為15個(gè)長(zhǎng)度單位=1532位=154字節(jié)。
保留
6比特,供以后應(yīng)用,現(xiàn)在置為0。
6個(gè)標(biāo)志位比特
1、URG:當(dāng)URG=1時(shí),注解此報(bào)文應(yīng)盡快傳送,而不要按本來(lái)的列隊(duì)次序來(lái)傳送。與“緊急指針”字段共同應(yīng)用,緊急指針指出在本報(bào)文段中的緊急數(shù)據(jù)的最后一個(gè)字節(jié)的序號(hào), 使接管方可以知道緊急數(shù)據(jù)共有多長(zhǎng)。 2、ACK:只有當(dāng)ACK=1時(shí),確認(rèn)序號(hào)字段才有效; 3、PSH:當(dāng)PSH=1時(shí),接收方應(yīng)該盡快將本報(bào)文段立即傳送給其應(yīng)用層。 4、RST:當(dāng)RST=1時(shí),表示出現(xiàn)連接錯(cuò)誤,必須釋放連接,然后再重建傳輸連接。復(fù)位比特還用來(lái)拒絕一個(gè)不法的報(bào)文段或拒絕打開(kāi)一個(gè)連接; 5、SYN:SYN=1,ACK=0時(shí)表示請(qǐng)求建立一個(gè)連接,攜帶SYN標(biāo)志的TCP報(bào)文段為同步報(bào)文段; 6、FIN:發(fā)端完成發(fā)送任務(wù)。
窗口
TCP通過(guò)滑動(dòng)窗口的概念來(lái)進(jìn)行流量控制。設(shè)想在發(fā)送端發(fā)送數(shù)據(jù)的速度很快而接收端接收速度卻很慢的情況下,為了保證數(shù)據(jù)不丟失,顯然需要進(jìn)行流量控制, 協(xié)調(diào)好
通信雙方的工作節(jié)奏。所謂滑動(dòng)窗口,可以理解成接收端所能提供的緩沖區(qū)大小。TCP利用一個(gè)滑動(dòng)的窗口來(lái)告訴發(fā)送端對(duì)它所發(fā)送的數(shù)據(jù)能提供多大的緩 沖區(qū)。窗口大小為
字節(jié)數(shù)起始于確認(rèn)序號(hào)字段指明的值(這個(gè)值是接收端正期望接收的字節(jié))。窗口大小是一個(gè)16bit字段,因而窗口大小最大為65535字節(jié)。
檢驗(yàn)和
檢驗(yàn)和覆蓋了整個(gè)TCP報(bào)文段:TCP首部和數(shù)據(jù)。這是一個(gè)強(qiáng)制性的字段,一定是由發(fā)端計(jì)算和存儲(chǔ),并由收端進(jìn)行驗(yàn)證。
緊急指針
只有當(dāng)URG標(biāo)志置1時(shí)緊急指針才有效。緊急指針是一個(gè)正的偏移量,和序號(hào)字段中的值相加表示緊急數(shù)據(jù)最后一個(gè)字節(jié)的序號(hào)。
三、TCP流量控制(滑動(dòng)窗口協(xié)議)
TCP流量控制主要是針對(duì)接收端的處理速度不如發(fā)送端發(fā)送速度快的問(wèn)題,消除發(fā)送方使接收方緩存溢出的可能性。
TCP流量控制主要使用滑動(dòng)窗口協(xié)議,滑動(dòng)窗口是接受數(shù)據(jù)端使用的窗口大小,用來(lái)告訴發(fā)送端接收端的緩存大小,以此可以控制發(fā)送端發(fā)送數(shù)據(jù)的大小,從而達(dá)到流量
控制的目的。這個(gè)窗口大小就是我們一次傳輸幾個(gè)數(shù)據(jù)。對(duì)所有數(shù)據(jù)幀按順序賦予編號(hào),發(fā)送方在發(fā)送過(guò)程中始終保持著一個(gè)發(fā)送窗口,只有落在發(fā)送窗口內(nèi)的幀才允許被發(fā)送;
同時(shí)接收方也維持著一個(gè)接收窗口,只有落在接收窗口內(nèi)的幀才允許接收。這樣通過(guò)調(diào)整發(fā)送方窗口和接收方窗口的大小可以實(shí)現(xiàn)流量控制。
我們可以通過(guò)下圖來(lái)分析:
?
?
?
?
1.發(fā)送方接收到了對(duì)方發(fā)來(lái)的報(bào)文 ack = 33, win = 10,知道對(duì)方收到了 33 號(hào)前的數(shù)據(jù),現(xiàn)在期望接收 [33, 43) 號(hào)數(shù)據(jù)。那我們開(kāi)始發(fā)送[33, 43) 號(hào)的數(shù)據(jù)。 2.[33, 43) 號(hào)的數(shù)據(jù)你是已經(jīng)發(fā)送了,但接受方并沒(méi)有接受到[36,37]數(shù)據(jù)。所以接收方發(fā)送回對(duì)報(bào)文段 A 的確認(rèn):ack = 35, win = 10。 3.發(fā)送方收到了 ack = 35, win = 10,對(duì)方期望接收 [35, 45) 號(hào)數(shù)據(jù)。那么發(fā)送方在發(fā)送[35, 45) 。
這里面需要思考一個(gè)問(wèn)題?
第一步發(fā)送了[33, 43),如果這次發(fā)送[35, 45),那中間重疊部分不是發(fā)送了兩次,所以這里要思考: 是全部重新發(fā)送還是只發(fā)送接收端沒(méi)有收到的數(shù)據(jù),如果全部發(fā)送那么重復(fù)
發(fā)送的數(shù)據(jù)接收端怎么處理。這個(gè)下面快速重傳會(huì)講。
4.接收方接收到了報(bào)文段 [35, 41),接收方發(fā)送:ack = 41, win = 10. (這是一個(gè)累積確認(rèn)) 5.發(fā)送方收到了 ack = 41, win = 10,對(duì)方期望接收 [41, 51) 號(hào)數(shù)據(jù)。 6. .......
這樣一直傳輸數(shù)據(jù),直到數(shù)據(jù)發(fā)送完成。這么一來(lái)就保證數(shù)據(jù)數(shù)據(jù)的可靠性,因?yàn)槿绻硵?shù)據(jù)沒(méi)有獲取到,那么ack永遠(yuǎn)不會(huì)跳過(guò)它。
這里也要思考一個(gè)問(wèn)題,如果某一數(shù)據(jù)一只沒(méi)有獲取到,總不能一直這樣堵塞在這里吧,這里就要講接下來(lái)有關(guān)堵塞的解決方法。
四、TCP擁塞控制
流量控制是通過(guò)接收方來(lái)控制流量的一種方式;而擁塞控制則是通過(guò)發(fā)送方來(lái)控制流量的一種方式。
TCP發(fā)送方可能因?yàn)镮P網(wǎng)絡(luò)的擁塞而被遏制,TCP擁塞控制就是為了解決這個(gè)問(wèn)題(注意和TCP流量控制的區(qū)別)。
TCP擁塞控制的幾種方法:慢啟動(dòng),擁塞避免,快重傳和快恢復(fù)。
這里先理解一個(gè)概念: 擁塞窗口
擁塞窗口:發(fā)送方維持一個(gè)叫做擁塞窗口 cwnd的狀態(tài)變量。擁塞窗口的大小取決于網(wǎng)絡(luò)的擁塞程度,并且動(dòng)態(tài)變化。
發(fā)送方的讓自己的發(fā)送窗口=min(cwnd,接受端接收窗口大?。?。說(shuō)明: 發(fā)送方取擁塞窗口與滑動(dòng)窗口的最小值作為發(fā)送的上限。
發(fā)送方控制擁塞窗口的原則是:只要網(wǎng)絡(luò)沒(méi)有出現(xiàn)擁塞,擁塞窗口就增大一些,以便把更多的分組發(fā)送出去。但只要網(wǎng)絡(luò)出現(xiàn)擁塞,擁塞窗口就減小一些,以減少
注入到網(wǎng)絡(luò)中的分組數(shù)。
下面將討論擁塞窗口cwnd的大小是怎么變化的。
1、慢啟動(dòng)
TCP在連接過(guò)程的三次握手完成后,開(kāi)始傳數(shù)據(jù),并不是一開(kāi)始向網(wǎng)絡(luò)通道中發(fā)送大量的數(shù)據(jù)包。因?yàn)榧偃缇W(wǎng)絡(luò)出現(xiàn)問(wèn)題,很多這樣的大包會(huì)積攢在路由器上,很容易導(dǎo)致網(wǎng)
絡(luò)中路由器緩存空間耗盡,從而發(fā)生擁塞。因此現(xiàn)在的TCP協(xié)議規(guī)定了,新建立的連接不能夠一開(kāi)始就發(fā)送大尺寸的數(shù)據(jù)包,而只能從一個(gè)小尺寸的包開(kāi)始發(fā)送,在發(fā)送和數(shù)據(jù)被
對(duì)方確認(rèn)的過(guò)程中去計(jì)算對(duì)方的接收速度,來(lái)逐步增加每次發(fā)送的數(shù)據(jù)量(最后到達(dá)一個(gè)穩(wěn)定的值,進(jìn)入高速傳輸階段。相應(yīng)的,慢啟動(dòng)過(guò)程中,TCP通道處在低速傳輸階段),
以避免上述現(xiàn)象的發(fā)生。這個(gè)策略就是慢啟動(dòng)。
畫(huà)個(gè)簡(jiǎn)單的圖從原理上粗略描述一下
?
?
?
我們思考一個(gè)慢啟動(dòng)引起的性能問(wèn)題?
在海量用戶高并發(fā)訪問(wèn)的大型網(wǎng)站后臺(tái),有一些基本的系統(tǒng)維護(hù)需求。比如遷移海量小文件,就是從一些機(jī)器拷貝海量小碎文件到另一些機(jī)器,來(lái)完成一些系統(tǒng)維護(hù)的基本需求。
慢啟動(dòng)為什么會(huì)對(duì)拷貝海量小文件的需求造成重大性能損失?
舉個(gè)簡(jiǎn)單的例子,我們對(duì)每個(gè)文件都采用獨(dú)立的TCP連接來(lái)傳輸(循環(huán)使用scp拷貝就是這個(gè)例子的實(shí)際場(chǎng)景,很常見(jiàn)的用法)。那么工作過(guò)程應(yīng)該是,每傳輸一個(gè)文件建立一個(gè)連接,然后連接處于慢啟動(dòng)階段,傳輸小文件,每個(gè)小文件幾乎都處于獨(dú)立連接的慢啟動(dòng)階段被傳輸,這樣傳輸過(guò)程所用的TCP包的總量就會(huì)增多。更細(xì)致的說(shuō)一說(shuō)這個(gè)事,如果在慢啟動(dòng)過(guò)程中傳輸一個(gè)小文件,我們可能需要2至3個(gè)小包,而在一個(gè)已經(jīng)完成慢啟動(dòng)的TCP通道中(TCP通道已進(jìn)入在高速傳輸階段),我們傳輸這個(gè)文件可能只需要1個(gè)大包。
網(wǎng)絡(luò)拷貝文件的時(shí)間基本上全部消耗都在網(wǎng)絡(luò)傳輸?shù)倪^(guò)程中(發(fā)數(shù)據(jù)過(guò)去等對(duì)端ACK,ACK確認(rèn)歸來(lái)繼續(xù)再發(fā),這樣的數(shù)據(jù)來(lái)回交互相比較本機(jī)的文件讀寫(xiě)非常耗時(shí)間),撇開(kāi)三次握手和四次握手那些包,如果文件的數(shù)量足夠大,這個(gè)總時(shí)間就會(huì)被放大到需求難以忍受的地步。
因此,在遷移海量小文件的需求下,我們不能使用“對(duì)每個(gè)文件都采用獨(dú)立的TCP連接來(lái)傳輸(循環(huán)使用scp拷貝)“這樣的策略,它會(huì)使每個(gè)文件的傳輸都處于在一個(gè)獨(dú)立TCP的慢啟動(dòng)階段。
?
如何避免慢啟動(dòng),進(jìn)而提升性能?
很簡(jiǎn)單,盡量把大量小文件放在一個(gè)TCP連接中排隊(duì)傳輸。起初的一兩個(gè)文件處于慢啟動(dòng)過(guò)程傳輸,后續(xù)的文件傳輸全部處于高速通道中傳輸,用這樣的方式來(lái)減少發(fā)包的數(shù)目,進(jìn)而降低時(shí)間消耗。同樣,實(shí)際上這種傳輸策略帶來(lái)的性能提升的功勞不僅僅歸于避免慢啟動(dòng),事實(shí)上也避免了大量的3次握手和四次握手,這個(gè)對(duì)海量小文件傳輸?shù)男阅芟囊卜浅V旅?/p>
2、擁塞避免
先補(bǔ)充下: 慢啟動(dòng)中擁塞窗口的cwnd值,開(kāi)始是1,接下開(kāi)是指數(shù)型增漲的。1、2、4、8、16.....這樣漲太快了吧。那么就有了堵塞避免。
cwnd不能一直這樣無(wú)限增長(zhǎng)下去,一定需要某個(gè)限制。TCP使用了一個(gè)叫慢啟動(dòng)門(mén)限(ssthresh)的變量,一旦cwnd>=ssthresh(大多數(shù)TCP的實(shí)現(xiàn),通常大小都是65536),慢啟動(dòng)過(guò)程結(jié)束,擁塞避免階段開(kāi)始;
擁塞避免:cwnd的值不再指數(shù)級(jí)往上升,開(kāi)始加法增加。此時(shí)當(dāng)窗口中所有的報(bào)文段都被確認(rèn)時(shí),cwnd的大小加1,cwnd的值就隨著RTT開(kāi)始線性增加,這樣就可以避免增長(zhǎng)過(guò)快導(dǎo)致網(wǎng)絡(luò)擁塞,慢慢的增加調(diào)整到網(wǎng)絡(luò)的最佳值。(它邏輯很簡(jiǎn)單就是到一定值后,cwnd不在是指數(shù)增長(zhǎng),而是+1增長(zhǎng)。這樣顯然慢多了)。
非ECN環(huán)境下的擁塞判斷,發(fā)送方RTO超時(shí),重傳了一個(gè)報(bào)文段,它的邏輯如下:
把ssthresh降低為cwnd值的一半。
把cwnd重新設(shè)置為1。
重新進(jìn)入慢啟動(dòng)過(guò)程。
?
?
?
上面的圖還是蠻好理解的。
3、快速重傳
TCP要保證所有的數(shù)據(jù)包都可以到達(dá),所以,必需要有重傳機(jī)制。
注意: 接收端給發(fā)送端的Ack確認(rèn)只會(huì)確認(rèn)最后一個(gè)連續(xù)的包,比如,發(fā)送端發(fā)了1,2,3,4,5一共五份數(shù)據(jù),接收端收到了1,2,于是回ack 3,然后收到了4(注意此時(shí)3沒(méi)收到)此時(shí)的TCP會(huì)怎么辦?我們要知道,因?yàn)檎缜懊嫠f(shuō)的,SeqNum和Ack是以字節(jié)數(shù)為單位,所以ack的時(shí)候,不能跳著確認(rèn),只能確認(rèn)最大的連續(xù)收到的包,不然,發(fā)送端就以為之前的都收到了。
3.1)超時(shí)重傳機(jī)制
一種是不回ack,死等3,當(dāng)發(fā)送方發(fā)現(xiàn)收不到3的ack超時(shí)后,會(huì)重傳3。一旦接收方收到3后,會(huì)ack 回 4——意味著3和4都收到了。
但是,這種方式會(huì)有比較嚴(yán)重的問(wèn)題,那就是因?yàn)橐赖?,所以會(huì)導(dǎo)致4和5即便已經(jīng)收到了,而發(fā)送方也完全不知道發(fā)生了什么事,因?yàn)闆](méi)有收到Ack,所以,發(fā)送方可能會(huì)悲觀地認(rèn)為也丟了,所以有可能也會(huì)導(dǎo)致4和5的重傳。
對(duì)此有兩種選擇:
一種是僅重傳timeout的包。也就是第3份數(shù)據(jù)。
另一種是重傳timeout后所有的數(shù)據(jù),也就是第3,4,5這三份數(shù)據(jù)。
這兩種方式有好也有不好。第一種會(huì)節(jié)省帶寬,但是慢,第二種會(huì)快一點(diǎn),但是會(huì)浪費(fèi)帶寬,也可能會(huì)有無(wú)用功。但總體來(lái)說(shuō)都不好。因?yàn)槎荚诘萾imeout,timeout可能會(huì)很長(zhǎng)。
3.2)快速重傳機(jī)制
于是,TCP引入了一種叫Fast Retransmit的算法,不以時(shí)間驅(qū)動(dòng),而以數(shù)據(jù)驅(qū)動(dòng)重傳。也就是說(shuō),如果,包沒(méi)有連續(xù)到達(dá),就ack最后那個(gè)可能被丟了的包,如果發(fā)送方連續(xù)收到3次相同的ack,就重傳。Fast Retransmit的好處是不用等timeout了再重傳,而是只是三次相同的ack就重傳。
比如:如果發(fā)送方發(fā)出了1,2,3,4,5份數(shù)據(jù),第一份先到送了,于是就ack回2,結(jié)果2因?yàn)槟承┰驔](méi)收到,3到達(dá)了,于是還是ack回2,后面的4和5都到了,但是還是ack回2因?yàn)?還是沒(méi)有收到,于是發(fā)送端收到了三個(gè)ack=2的確認(rèn),知道了2還沒(méi)有到,于是就馬上重轉(zhuǎn)2。然后,接收端收到了2,此時(shí)因?yàn)?,4,5都收到了,于是ack回6。
示意圖如下
?
?
Fast Retransmit只解決了一個(gè)問(wèn)題,就是timeout的問(wèn)題,它依然面臨一個(gè)艱難的選擇,就是重轉(zhuǎn)之前的一個(gè)還是重裝所有的問(wèn)題。對(duì)于上面的示例來(lái)說(shuō),是重傳#2呢還是重傳#2,#3,#4,#5呢?因?yàn)榘l(fā)送端并不清楚這連續(xù)的3個(gè)ack(2)是誰(shuí)傳回來(lái)的?也許發(fā)送端發(fā)了20份數(shù)據(jù),是#6,#10,#20傳來(lái)的呢。這樣,發(fā)送端很有可能要重傳從#2到#20的這堆數(shù)據(jù)(這就是某些TCP的實(shí)際的實(shí)現(xiàn))??梢?jiàn),這是一把雙刃劍。
總結(jié): 不管是超時(shí)重傳還是快速重傳確實(shí)能保證數(shù)據(jù)的可靠性,但它無(wú)法解決的問(wèn)題就是:比如發(fā)送端發(fā)了1、2、3、4、5,而接收端收到了1、3、4、5,那么這個(gè)時(shí)候它發(fā)送的ack是2。那么發(fā)送端發(fā)送的是重傳#2呢還是重傳#2,#3,#4,#5的問(wèn)題。如果在發(fā)送#2,#3,#4,#5,本身資源是一種浪費(fèi),因?yàn)榻邮芊?3,#4,#5已經(jīng)緩存下來(lái),只需#2,所以在發(fā)一遍是無(wú)意義的。
評(píng)論
查看更多