TCP通過校驗和、序列號、確認應答、重發控制、連接管理以及窗口控制等機制實現可靠性的傳輸。
先來看第一個可靠性傳輸的方法。
通過序列號和可靠性提供可靠性
TCP是面向字節的。TCP把應用層交下來的報文(可能要劃分為許多較短的報文段)看成一個一個字節組成的數據流,并使每一個字節對應一個序號。在建立連接時,雙方TCP要各自確定初始序列號。TCP每次發送的報文段的首部中的序列字段數值表示該報文段中首部后面的第一個數據字節的序號。
TCP使用的是累計確認,即確認是對所有按序接收到的數據的確認。需要注意的是,接收方返回的確認號是已按序收到的數據的最高序號加1,也就是說,確認號表示接收方期望下次收到的數據中的第一個數據字節的序號。
當發送端的數據到達接收主句時,接收端主機會返回一個已收到消息的通知。這個消息叫做確認應答(已指已經接收)。就相當于別人對你發了一條消息,你要回復一句,這樣對方才知道你已經收到了消息。
TCP通過肯定的確認應答(ACK)實現可靠的數據傳輸。當發送端將數據發出之后會等待對端的確認應答。如果沒有確認應答,說明數據已經成功到達對端。反之,則數據丟失的可能性很大。
如下圖所示,在一定的時間內要是沒有等到確認應答,發送端就可以認為數據已經丟失,并進行重發。由此,即使產生了丟包,仍然能夠保證數據能夠到達對端,實現可靠傳輸。
未收到確認應答并不意味著數據一定丟失。也可能是數據對方已經收到,只是返回的確認應答在途中丟失。這種情況也會導致發送端因沒有收到確認應答,而認為數據沒有到達目的地,從而進行重發。如圖所示:
圖示的情況是,由主機B返回的確認應答,因網絡原因在傳送帶途中丟失,沒有到達主機A。主機A會等待一段時間,若在特定的時間間隔內始終沒有收到確認應答,主機A就會對此數據進行重發。此時,主機B將第二次發送已接收此數據的確認應答。由于主機B其實已經收到過1~1000的數據,當再有相同數據送達時它會放棄。
此外,也有可能因為一些其他原因導致確認應答延遲到達,在源主機重發數據以后才到達的情況也有。此時,源發送主機只要按照機制重發數據即可。但是對于目標主機來說,這簡直是一種“災難”。它會反復收到相同的數據。而為了對上層應用提供可靠的傳輸,必須得放棄重發的數據包而且要立即發回確認信息。為此,就必須引入一種機制,它能夠識別是否已經接收數據,又能夠判斷是否需要接收。
上述這些確認應答處理、重發控制以及重復控制等功能都可以通過序列號實現。序列號是按順序給發送數據的每一個字節都標上號碼的編號。接收端查詢接收數據TCP首部中的序列號和數據的長度,將自己下一步應該接收到序號作為確認應答返送回去。就這樣,通過序列號和確認應答號,TCP可以實現可靠傳輸。例如,已經收到了1700號、8011000號和12011500號,而701800號及1001~1200號的數據還沒有收到,那么這時發送的確認號應為701號。
TCP的數據長度并未寫入TCP首部。實際通信中求得TCP包的長度的計算公式是:IP首部中的數據包長度 — IP首部長度TCP首部長度。
重復超時如何確定
重發超時是指在重復數據之前,等待確認應答到來的那個特定時間間隔。如果超過了這個時間仍未收到確認應答,發送端將數據進行數據重發。那么這個重發超時的具體時間長度又是如果確定的呢?
最理想的是,找到一個最小的時間,它能保證“確認應答一定能在這個時間內返回”。然而這個時間長短隨著數據包途徑的網絡環境的不同而有所變化。例如在高速的LAN中時間較短,而在長距離的通信當中應該比LAN要長一些。即使是在同一個網絡中,根據不同時段的網絡擁堵程度時間的長短也會發送變化。
重發超時的計算既要考慮往返時間又要考慮偏差是有原因的,如下圖所示,根據網絡環境的不同往返時間可能會產生大幅度的搖擺,之所以發生這種情況是因為數據包的分段是經過不同線路到達的。TCP/IP的目的是即使在這種環境下也要進行控制,盡量不要浪費網絡流量。
在BSD的Unix以及Windows系統中,超時都以0.5秒為單位進行控制,因此重發超時都是0.5秒的整數倍。不過,由于最初的數據還不知道往返時間,所以其重發超時一般設置為6秒左右。
數據被重發之后若還是收不到確認應答,則進行再次重發。此時,等待確認應答的時間會以2倍、4倍的指數函數延長。但是也不會無限重發。達到一定重發次數后,如果仍沒有確認應答返回,就會判斷網絡或對端主機發生了異常,強制關閉連接。并且通知應用通信異常強行終止。
TCP以段位單位發送數據
在建立TCP連接的同時,也可以確定發送數據包的單位,我們可以稱為“最大消息長度”(MSS:Maximum Segment Size)。最理想的情況是,最大消息長度正好是IP中不會被分片處理的最大數據長度。
TCP在傳送大量數據時,是以MSS的大小將數據進行分割發送。進行重發時也是以MSS為單位。
MSS是在三次握手的時候,在兩端主機之間被計算得出。兩端的主機在發出建立連接的請求時,會在TCP首部中寫入MSS選項,告訴對方自己的接口能夠適應MSS的大小。然后會在二者之間選擇一個較小的值使用。
為附加MSS選項,TCP首部將不再是20字節,而是4字節的整數倍,如圖所示的+4。
1和2通過建立連接的SYN包相互通知對方網絡接口的MSS值。3在兩者之間選一個較小的作為MSS的值,發送數據。
滑動窗口
利用窗口控制提高速度
TCP以1個段為單位,每發一個段進行一次確認應答的處理。這樣的傳輸方式有一個缺點,那就是,包的往返時間越長通信性能就越低,網絡的吞吐量會越差。
為了解決這個問題,TCP引入了窗口這個概念。即使在往返時間較長的情況下,它也能控制網絡性能的下降。如圖所示,確認應答不再是以每個分段,而是以更大的單位進行確認時,轉發時間將會被大幅度的縮短。也就是說,發送端主機,在發送了一個段以后不必要一直等待確認應答,而是繼續發送。
根據窗口為4000字節時返回的確認應答,下一步就發送比這個值還要大4000個序列號為止的數據。這跟前面每個段接收確認應答以后再發送另一個新段的情況相比,即使往返時間變長也不會影響網絡的吞吐量。
窗口大小就是指無需等待確認應答而可以繼續發送數據的最大值。上圖中,窗口大小為4個段,每個段是1000字節。
這個機制實現了使用大量的緩沖區,通過對多個段同時進行確認應答的功能。
如下圖所示,發送數據中高亮圈起來的部分正是剛才提到的窗口。在這個窗口內的數據即便沒有收到確認應答也可以發送出去。此外,從該窗口中能看到的數據因其某種數據已在傳輸中丟失,所以發送端才能收到確認應答,這種情況也需要重發。為此,發送端主機在等到確認應答返回前,必須在緩沖區中保留這部分數據。
- 滑動窗口的方式
在1的狀態下,如果收到一個請求序列號為2001的確認應答,那么2001之前的數據就沒有必須必須進行重發,這部分的數據可以被過濾掉,滑動窗口成為3的樣子。
窗口控制與重發控制
在使用窗口控制中,要是出現段丟失該怎么辦?
首先,我們先考慮確認應答未能返回的情況。在這種情況下,數據已經到達對端,是不需要再進行重發的。然而,在沒有使用窗口控制的時候,沒有收到確認應答的數據都會被重發。而使用了窗口控制,某些確認應答即便丟失也無需重發。
窗口在一定程序上較大時,即使有少部分的確認應答丟失也不會進行數據重發。可以通過下一個確認應答進行確認。
其次,我們來分析一下某個報文段丟失的情況。如下圖所示,接收主機如果收到一個自己應該接收到序號以外的數據時,會針對當前為止收到數據返回確認應答。
如圖所示,當某一報文丟失后,發送端會一直收到序號為1001的確認應答,這個確認應答好像在提醒發送端“我想接收到是1001開始的數據”。因此,在窗口比較大,又出現報文段丟失的情況下,同一個序號的確認應答將會被重發不斷的返回。而發送端主機如果連續3次收到同一個確認應答,就會將其所對應的數據進行重發。這種機制比之前的超時管理更加高效,因此也被稱為高速重發機制。
接收端在沒有收到自己所期望序號的數據時,會對之前收到的數據進行確認應答。發送端則一旦收到某個確認應答后,又連續3次收到同樣的確認應答,則認為數據段已經丟失,需要進行重發。
流量控制
發送端根據自己的實際情況發送數據。但是,接收端可能收到的是一個毫無關系的數據包又可能會在處理其它問題上花費一些時間。因此在為這個數據包做其他處理時會耗費一些時間,甚至在高負荷的情況下無法接收任何數據。如此一來,如果接收端本應該接收端數據丟棄的話,就又會觸發重發機制,從而導致網絡流量的無端浪費。
為了防止這種現象的發生,TCP提供一種機制可以讓發送端根據接收端的實際接收能力控制發送端數據量。這就是所謂的流量控制。它的具體操作是,接收端主機向發送端主機通知自己可以接收數據的大小,于是發送端會發送不超過這個限度的數據。該大小限度就被稱為窗口大小,窗口大小的值由接收端主機決定的。
TCP首部中,專門有一個字段用來通知窗口大小。接收主機將自己可以接收端緩沖區大小放入這個字段中通知發送端。這個字段的值越大,說明網絡的吞吐量越高。不過,接收端的這個緩沖區一旦面臨數據溢出時,窗口大小的值也會隨之被設置為一個更小的值通知給發送端,從而控制數據發送量。也就是說,發送端主機會根據接收端主機的指示。對發送數據的量進行控制。這也就形成了一個完整的TCP流控制。
圖為根據窗口大小控制流量過程的示例。
如圖所示,當接收端收到從3001號開始的數據段后其緩沖區即滿,不得不暫時停止接收數據。之后,在收到發送窗口更新通知后通信才得以繼續進行。如果這個窗口的更新通知再傳輸途中丟失,可能還會導致無法繼續通信。為避免此類問題的發生,發送端主機會時不時的發送一個叫做窗口探測的數據段,此數據段僅含一個字節以獲取最新的窗口大小信息。
擁塞控制
一般來說,計算機網絡處在一個共享的環境。因此也有可能會因為其他主機之間的通信使得網絡擁堵。在網絡出現擁堵時,如果突然發送一個較大量的數據,極有可能會導致整個網絡的癱瘓。
TCP為了防止這種問題的出現,在通信一開始時就會通過一個叫做慢啟動的算法得出的數值,對發送數據量進行控制。
首先,為了在發送端調節所要發送數據的量,定義了一個叫做“擁塞窗口”的概念。于是在慢啟動的時候,將這個擁塞窗口的大小設置為1個數據段發送數據,之后每收到一次確認應答(ACK),擁塞窗口的值就加1.在發送數據包時,將擁塞窗口的大小與接收端主機通知的窗口大小做比較,然后按照它們當中較小的那個值,發送比其還要小的數據量。
如果采用超時機制,那么擁塞窗口的初始值可以設置為1以后再進行慢啟動修正。有了上述這些機制,就可以有效的減少通信開始時聯系發包導致的網絡擁堵,還可以避免網絡擁堵的發生。
不過,隨著包的每次往返,擁塞窗口也會以1、2、4等指數函數的增長,擁堵狀況激增甚至會導致網絡擁塞的發生。為了防止這些,引入了慢啟動閾值的概念。只要擁塞窗口的值超出這個閾值,在每收到一次確認應答時,只允許下面這種比例放大擁塞窗口。
1個數據段的字節數擁塞窗口(字節)×1個數據段字節數 \frac{1個數據段的字節數}{擁塞窗口(字節)}×{1個數據段字節數}擁塞窗口(字節)1個數據段的字節數?×1個數據段字節數
擁塞窗口越大,確認應答的數目也會增加。不過隨著每收到一個確認應答,其漲幅也會逐漸減少,甚至小過比一個數據段還要小的字節數。因此,擁塞窗口的大小會呈直線上升的趨勢。
TCP的通信開始時,并沒有設置相應的慢啟動閾值。而是在超時重發時,才會設置為當時擁塞窗口的一半大小。
由重發確認應答而觸發的高速重發與超時重發機制的處理多少有些不同。因為前者要求至少三次的確認應答數據段到達對方主機后才會觸發,相比后者網絡的擁堵要輕一些。
而由重復確認應答進行高速重發控制時,慢啟動閾值的大小被設置為當時窗口大小的一半。然后將窗口的大小設置為慢啟動閾值+3個數據段的大小。
有了這樣一種控制,TCP的擁塞窗口如上圖所示發送變化。由于窗口的大小會直接影響數據被轉發時的吞吐量,所以一般情況下,窗口越大,越會形成高吞吐量的通信。
當TCP通信開始以后,網絡吞吐量會逐漸上升,但是隨著網絡擁堵的發生吞吐量也會急速下降。于是會再次進入吞吐量慢慢上升的過程。因此所謂TCP的吞吐量的特點就好像在逐步占領網絡寬度的感覺。
延遲確認應答
接收數據的主機如果每次都立刻回復確認應答的話,可能會返回一個較小的窗口。那是因為剛接收完數據,緩沖區已滿。
當某個接收端收到這個小窗口的通知以后,會以它為上限發送數據,從而又降低了網絡的利用率。為此,引入了一個方法,那就是收到數據以后并不立即返回確認應答,而是延遲一段時間的機制。
事實上,大可不必為每一個數據段進行一次確認應答。TCP采用滑動窗口的控制機制,因此通常確認應答少一些也無妨、TCP文件傳輸中,絕大多數是每兩個數據段返回一次確認應答。
捎帶應答
捎帶應答是指TCP的確認應答和回執數據可以通過一個包發送。通過這種機制,可以使收發的數據量減少。
另外,接收數據以后如果立刻返回確認應答,就無法實現捎帶應答。而是將所接收到數據傳給應用處理生成返回數據以后再進行發送請求為止,必須一直等待確認應答的發送。也就是說,如果沒有啟用延遲確認應答就無法實現捎帶應答。延遲確認應答是能夠提高網絡利用率從而降低計算機處理負荷的一種較優的處理機制。