上篇文章,我們講了TCP協議的連接管理(”三次握手“和”四次揮手“的過程)。
4、滑動窗口
這個滑動窗口是TCP中非常有特點的機制。我們知道,TCP是通過前面講的三個機制:確認應答,超時重傳,連接管理,實現了可靠傳輸。
但是,為了實現可靠傳輸,同時也付出代價,單位時間傳輸的數據量變少了(傳輸效率降低了)。
在確認應答機制下,每次發送方收到一個ack才會發送下一個數據,就會導致有大量時間都消耗在等待ack上了,此處等待消耗的時間成本是非常多的。我們希望的是,在保證可靠傳輸的前提下,盡可能讓效率高些,讓消耗的時間成本少些。
滑動窗口這個機制就是為了解決上述問題的,滑動窗口可以在保證可靠傳輸的基礎上,提高效率。(這里的提高效率,其實是降低了時間的損耗,而不是增加了傳輸速度)。
雖然通過滑動窗口這個機制,一定程度上提高了效率,但還是不可能高于UDP這種不需要可靠性的協議。?
滑動窗口,其實就是批量傳輸:發一個數據,然后不等ack,再發下一條,繼續往下發,連續發了一定的數據之后,統一等一波ack。——把多次請求等待ack的時間,使用同一份時間進行等待了,減少了總的等待時間。
為啥會叫滑動窗口呢?—— 批量傳輸數據的過程,就像下圖窗口滑動的過程,返回一個ack,就窗口就往后移動(發送下一組數據)。?
在這幅圖上面,黃色區域中的四份數據已經批量傳輸出去了,傳輸出這四份數據之后,就等待ack?,暫時先不傳輸數據了。我們就把黃色區域(不等待ack,能夠批量傳輸的數據量)稱為“窗口大小”。
注意:這里并不是等所有ack回來再開始發下一組數據(5001~6001)的,而是等回來一個ack就開始往后發一組數據。
上述發送/返回ack過程都會很快,窗口快速地向后面移動,也就形成一個“滑動”的效果了。因此就把上述過程稱為滑動窗口了。
出現丟包的處理
滑動窗口是我們為了提升傳輸效率的一個機制,但TCP安身立命之本還是可靠性。提升效率的大前提一定是傳輸是可靠的,如果“滑動窗口”傳輸的過程中丟包會這么處理呢?
情況一:數據報已經到達,但是ACK丟包了
如上圖,主機A的數據都是完好的傳到了主機B,但是主機B返回的一些ack出現丟包了,這種情況并不會對我們數據的傳輸造成影響,我們無需進行任何處理。
注意:這里返回ack 確認序號的含義,比如說返回一個5001,代表5001之前的數據,我(主機B)都已經收到了,就相當于告訴主機A 4001 之前的數據我也已經收到了。
情況二:數據報丟了
數據丟了的情況,此時就需要重傳了
如上圖1001~2000的數據報丟失了,解決這份數據報丟失的關鍵要點:在主機A繼續向發送數據的時候,主機B的應答報文中返回的確認序號是1001,相當于在向對方索要1001~2000的數據。當發送方向接收方重傳了1001~2000的數據報之后(此時已經傳到3001~4000的數據了),確認序號就變為了當前所傳數據的下一序號(4001)。
在上述重傳的過程中,整體的效率是非常高的,這里做到了“針對性重傳”。只需要把那個丟了的數據重傳,已經收到的數據就不必重傳了,我們就把這種重傳稱為“快速重傳”。
注意:
我們前面講的幾個機制:確認應答、超時重傳、滑動窗口、快速重傳,他們是并不沖突的。
?
滑動窗口中當然也有確認應答,只不過把等待策略稍作調整,轉成批量的了。批量的前提是:你短時間內發了很多數據。如果你發的數據很少,此時滑動窗口滑不起來,就變成了確認應答。
如果當前傳輸過程是按照滑動窗口(短時間內傳輸了大量數據)就按照快速重傳保證可靠性,此時判定丟包的標準就是看有連續多個ack索要同一個數據。
如果當前傳輸過程是不是按照滑動窗口(沒有傳輸很多數據),此時仍然按照之前的超時重傳保證數據的可靠性,此時判定丟包的標準就是達到超時時間還沒有ack到達。
5、流量控制
我們前面講,通過滑動窗口,可以提高傳輸速率,窗口大小越大,就有更多的數據復用同一塊時間等待ack,效率就越高。
窗口大小是不可能無限大的,因為TCP的安生立命之本是可靠傳輸,任何提升效率的行為,都不能影響可靠性(如果接收方的接收緩沖區飽和了,你繼續再給他發就會丟包了,此時重傳也可能沒用,反而會浪費硬件資源)。
這就像我們小學數學的蓄水池問題:?與其等接收方的接收緩沖區滿了,發送方再停止發送,還不如讓發送方提前感知到接收方接收緩沖區的情況,提前減慢發送速度,讓發送方發送數據和接收方處理數據的速度,可以做到步調一致。這也就是流量控制。
在TCP報頭中,有一個16位窗口大小,就是用來做這個的:
通過這個字段來給發送方反饋接收方的接收能力(接收方接收緩沖區的剩余大小),發送方就會知道接下來的窗口大小要設置成多少合適。
補充:
我們這里TCP報頭中,窗口大小是16位,但窗口大小并非是64KB(2的16次方是65535,64KB = 64*1024 = 65535),實際上,TCP報頭選項中,還包含了一個參數,叫窗口擴展因字,實際上真是要設置的窗口大小是:16位窗口大小*2^窗口擴展因子。
如上圖,當接收方的接收緩沖區為0的時候,此時接收方就應該暫停發送,發送方什么時候恢復發送呢?發送方會周期性地發送一個"窗口探測包",這個窗口探測包,并不會攜帶任何載荷,這樣地包對于業務并不產生影響,只是為了觸發接收方的ack,一旦查詢出的結果是非0時,就什么緩沖區又有空位了,發送方就又可以繼續發送了。
?