1. 程式人生 > >網路基本功系列:細說網路那些事兒(3月26日更新)

網路基本功系列:細說網路那些事兒(3月26日更新)

網路基本功(十):細說TCP確認機制

介紹

在TCP確認機制中,無法有效處理非連續TCP片段。確認號表明所有低於該編號的sequence number已經被髮送該編號的裝置接收。如果我們收到的位元組數落在兩個非連續的範圍內,則無法只通過一個編號來確認。這可能導致潛在嚴重的效能問題,特別是高速或可靠性較差的網路。

更多資訊

還是以下圖為例,伺服器傳送了4個片段並收到1條回覆,確認號為201。因此,片段1和片段2被當成已確認。它們從重傳佇列中移出,同時允許伺服器傳送視窗向右移動200位元組,從而傳送資料增加200個位元組。

然而,再次假設片段3,從sequence number201開始,在傳送過程中丟失了。由於客戶端從沒有收到這一片段,所以它也無法傳送確認號高於201的確認資訊,從而導致滑動視窗停滯。伺服器可以繼續傳送其他片段直到填滿客戶端的接收視窗,但是直到客戶端傳送另一條確認資訊,伺服器的傳送視窗都不會滑動。

另一個問題是如果片段3丟失了,客戶端將無法告知伺服器是否收到後續的片段。在客戶端接收視窗填滿之前,很有可能客戶端已經接收到片段4以及之後的片段。但是客戶端無法傳送值為501的確認資訊以表明接收到片段4,因為這意味著片段3也接收到了

image002.jpg

這裡我們看到了TCP單編號,累積確認機制的缺點。我們可以想象一個最差的情況,伺服器被告知它有一個10,000位元組視窗,20個片段每個片段500位元組。第一個片段丟失了,其他19個被接收到了。但是由於第一個片段從沒有接收到,其他19個也無法確認。

未確認片段處理策略:

我們怎樣處理丟失片段之後的片段呢?本例中,當伺服器片段3重傳超時,它必須決定怎樣處理片段4,它不知道客戶端是否已經接收到。在上述最差情況下,第一個片段丟失後,其餘19個可能或可能無法被客戶端接收到。

處理這種情況有兩種可能的方式:

僅重傳超時片段:這是一種更加保守的方式,僅重傳超時的片段,希望其他片段都能夠成功接收。如果該片段之後的其他片段實際上接收到了,這一方式是最佳的,如果沒接收到,就無法正常執行。後者的情況每一個片段需要單獨計時並重傳。假設上述最壞情況下,所有20個500位元組片段都丟失了。我們需要等片段1超時並重傳。這一片段也許會得到確認,但之後我們需要等待片段2超時並重傳。這一過程會重複多次。

重傳所有片段:這是一種更激進或者說更悲觀的方式。無論何時一個片段超時了,不僅重傳該片段,還有所有其他尚未確認的片段。這一方式確保了任何時間都有一個等待確認的停頓時間,在所有未確認片段丟失的情況下,會重新整理全部未確認片段,以使對端裝置多一次接收機會。在所有20個片段都丟失的情況下,相對於第一種方式節省了大量時間。這種方式的問題在於可能這些重傳是不必要的。如果第一個片段丟失而其他19個實際上接收到了,也得重傳那9500位元組資料。

由於TCP不知道其他片段是否接收到,所以它也無法確認哪種方法更好,但只能選擇一種方式。上圖示例了保守的方式,而下圖顯示的是激進的方式:

image003.jpg

問題的關鍵在於無法確認非連續片段。解決方式是對TCP滑動視窗演算法進行擴充套件,新增允許裝置分別確認非連續片段的功能。這一功能稱為選擇確認(selective acknowledgment, SACK)。

選擇確認:

通過SACK,連線的兩方裝置必須同時支援這一功能,通過連線時使用的SYN片段來協商是否允許SACK。這一過程完成之後,任一裝置都可以在常規TCP片段中使用SACK選項。這一選項包含一個關於已接收但未確認片段資料sequence number範圍的列表,由於它們是非連續的。

各裝置對重傳佇列進行修改,如果該片段已被選擇確認過,則該片段中的SACK位元位置為1。該裝置使用圖2中激進方式的改進版本,一個片段重傳之後,之後所有片段也會重傳,除非SACK位元位為1

例如,在4個片段的情況下,如果客戶端接收到片段4而沒有接收到片段3,當它發回確認號為201(片段1和片段2)的確認資訊,其中包含一個SACK選項指明:“已接收到位元組361至500,但尚未確認”。如果片段4在片段1和2之後到達,上述資訊也可以通過第二個確認片段來完成。伺服器確認片段4的位元組範圍,併為片段4開啟SACK位。當片段3重傳時,伺服器看到片段4的SACK位為1,就不會對其重傳。如下圖所示。

在片段3重傳之後,片段4的SACK位被清除。這是為了防止客戶端出於某種原因改變片段4已接收的想法。客戶端應當傳送確認號為501或更高的確認資訊,正式確認片段3和4接收到。如果這一情況沒有發生,伺服器必須接收到片段4的另一條選擇確認資訊才能將它的SACK位開啟,否則,在片段3重傳時或計時器超時的情況下會對其自動重傳。

image004.jpg

網路基本功(十一):TCP視窗調整與流控

介紹

前文已經介紹過了TCP滑動視窗大小的重要性。在客戶端與伺服器的連線中,客戶端告知伺服器它一次希望從伺服器接收多少位元組資料,這是客戶端的接收視窗,即伺服器的傳送視窗。類似地,伺服器告知客戶端一次希望從客戶端接收多少位元組資料,也就是伺服器的接收視窗和客戶端的傳送視窗。

要理解為什麼視窗大小會產生波動,首先需要理解它的含義。最簡單的方式是它代表了裝置對於特定連線的接收快取大小。即,視窗大小代表一個裝置一次能夠從對端處理多少資料,之後再傳遞給應用層處理。

更多資訊

當伺服器從客戶端接收資料,它就將資料放在快取中,伺服器必須對資料做以下兩步操作:

確認:伺服器必須將確認資訊發回客戶端以表明資料接收。

傳輸:伺服器必須處理資料,將它傳遞給目標應用程式處理。

區分開這兩件事情是非常重要的。關鍵在於基本的滑動視窗機制中,資料於接收時確認,但並不一定立即從快取中傳輸出去。也就意味著當接收資料速度快於接收TCP處理速度時,快取有可能被填滿。當這一情況發生時,接收裝置需要調整視窗大小已防止快取過載。

由於視窗大小能夠以這種方式管理連線兩端裝置資料流的速率,TCP就是以這種方式實現流控這一傳輸層非常典型的任務。流控對於TCP來說是很重要的,因為它是裝置間互通狀態的方式。通過增加或縮小視窗大小,伺服器和客戶端能夠確保對端傳送資料的速度等同於處理速度。

減小視窗大小以降低傳送速率:

首先看一下客戶端到伺服器的資料傳輸,如下圖所示。

image002.jpg

客戶端傳輸140位元組資料至伺服器。之後,客戶端的可用視窗還剩下220位元組:傳送視窗的360位元組減去傳送的140位元組。

一段時間過後,伺服器接收到140位元組並將它們放在快取中。現在,理想的情況下,140位元組進入快取,確認之後立刻從快取移出。也就是說,快取有足夠的大小來容納客戶端傳送的所有資料。快取的空閒空間維持在360位元組,因此告知客戶端視窗大小保持不變。

只要伺服器處理速度和資料進入速度相同,視窗大小就會保持在360位元組。客戶端在接收到140位元組的確認資訊以及視窗大小保持不變的資訊之後,將360位元組視窗向右移動140位元組。由於現在未確認位元組數為0,因此客戶端又可以傳送360位元組資料。對應於之前可用視窗的220位元組,加上剛剛確認的140位元組資料。

然而,現實中伺服器可能需要處理數十,數百乃至數千個TCP連線。TCP可能無法立刻處理資料,或應用應用程式本身無法接收140位元組資料。任何一種情況下,伺服器TCP都無法立刻將140位元組從快取中移出。這時,除了發回確認資訊給客戶端以外,伺服器會想要告知客戶端更改視窗大小,以表示快取已經被部分寫入了。

假設我們接收到140位元組,但只能傳送40位元組給應用程式,快取中剩下100位元組。當傳送140位元組的確認資訊,伺服器將傳送視窗縮小100位元組,至260位元組。當客戶端從伺服器接收到這一片段,它將會看到140位元組的確認資訊並將視窗向右滑動140位元組。在滑動過程中,將大小縮減至260位元組。可以認為將視窗左端滑動140位元組,但右端僅滑動40位元組。新的稍小一些的視窗保證伺服器從客戶端接收最多260位元組資料,以適應接收快取中的剩餘空間,如下圖的1-3步所示。

image003.jpg

縮減傳送視窗以停止傳送新資料:

如果伺服器無法接收任何新資料會怎麼樣呢?假設客戶端下一次傳輸180位元組,但是伺服器太忙碌而無法對其進行處理。這種情況下,伺服器將這180位元組快取下來,並且在確認資訊中,將視窗大小從260位元組縮減為80位元組。當客戶端接收到180位元組的確認資訊,它也會看到視窗縮減了180位元組,它會滑動與縮減同樣的大小,告知伺服器:我確認接收180位元組資料,但不允許你再發送新的資料。也可以看作視窗左端滑動180位元組,但右端維持不動。只要右端不移動,客戶端就無法傳送更多資料。這一過程顯示在上圖的4-6中。

關閉傳送視窗:

視窗調整可以通過雙方裝置來完成。如果伺服器從客戶端接收的資料持續快於推送給應用的速率,則伺服器將會繼續減小接收視窗。假設傳送視窗減小至80位元組,客戶端傳送第三個請求,長度為80位元組,但伺服器仍處於繁忙狀態。之後伺服器將視窗減小為0,也稱為關閉視窗。這一資訊告知客戶端伺服器已經過載,它需要徹底停止傳送資料,如上圖最後一步所示。之後,當伺服器負載減輕時,可以再次增加這一連線的視窗,允許更多資料傳輸。