1. 程式人生 > >TCP流量控制中的滑動視窗大小、TCP欄位中16位視窗大小、MTU、MSS、快取區大小有什麼關係

TCP流量控制中的滑動視窗大小、TCP欄位中16位視窗大小、MTU、MSS、快取區大小有什麼關係

本文將涉及到IP、TCP、Socket纏綿悱惻的愛情故事,如果您依然相信愛情,請耐心地看下去…





MTU: Maximum Transmit Unit,最大傳輸單元,即物理介面(資料鏈路層)提供給其上層(通常是IP層)最大一次傳輸資料的大小;以普遍使用的乙太網介面為例,預設MTU=1500 Byte,這是乙太網介面對IP層的約束,如果IP層有<=1500 byte 需要傳送,只需要一個IP包就可以完成傳送任務;如果IP層有> 1500 byte 資料需要傳送,需要分片才能完成傳送,這些分片有一個共同點,即IP Header ID相同。

MSS:Maximum Segment Size ,TCP提交給IP層最大分段大小,不包含TCP Header和 TCP Option,只包含TCP Payload ,MSS是TCP用來限制application層最大的傳送位元組數。如果底層物理介面MTU= 1500 byte,則 MSS = 1500- 20(IP Header) -20 (TCP Header) = 1460 byte,如果application 有2000 byte傳送,需要兩個segment才可以完成傳送,第一個TCP segment = 1460,第二個TCP segment = 540。


A (MTU 1500) <------Internet ------> (MTU 1492) B

見上圖,TCP SYN訊息,A 傳送給B 的MSS= 1460,告訴B,B發給A最大segment 為1460 byte

TCP SYN訊息,B傳送給A的MSS= 1452,告訴A,A發給B最大segment 為1452 byte 。

但A最終能一次發給B多大的位元組的segment呢???我們給它取名為:A_Send_MSS,取決於兩個值,一個是B的通告MSS= 1452;另一個是本地物理介面MTU的限制:1500-20-20= 1460。取這兩者較小的一個值,則
A_Send_MSS = minimum ( 1452, 1460) = 1452


同理可得 
B_Send_MSS = minimum ( 1460,1452)=1452

可以看出這兩者最後是相同的,所以可以得到以下結論,通訊雙方最終的MSS = 雙方較小MTU- 40 。



Socket傳送和接收緩衝區大小:按照我的理解,socket將TCP實現細節封裝成API,Socket雖然不等於TCP,但TCP的 send_buffer 和 receive_buffer 應該和socket 等同,用C語言的說法,就是指標相同,用通俗語言來說就是,相同的記憶體地址段。接收緩衝區receive_buffer 大小等同於自己通告的window size; 至於send_buffer 大小會影響application放入傳送緩衝區的速率,但是它應該不是瓶頸,所以不再討論它。



TCP Window Size: 如果A傳送給B window size = 8192,意思是:B最多可以連續傳送8192 byte 給A,在A ACK這8192 byte之前。那A的這個8192byte 怎麼來的呢? 一般來說,8192byte就是A的接收緩區,A_Receive_Buffer= 8192,如果B不小心傳送超過8192 byte,如果application沒有及時取走,則超過8192 byte 資料可能會因為A_Receive_Buffer滿而被丟棄,所以B會嚴格遵守A的 advertised window size,如果A通告的window = 0,則B一定不會發送資料。

Window Size 佔兩個byte,最大值為65535,看完下文你會發現它對對方傳送速率影響很大。如果window size 是高速網路頻寬的瓶頸,可以採用TCP Option scaling window 這個選項。


Persist Timer: 用於週期探測對方advertised window size 是否依然為0的定時器。接上文,如果A通告的window = 0,則B一定不會發送資料。那B會不會一直被動地等下去?大家想象一個這樣的場景,如果B一直被動等待,而這時A接收緩衝區已經空了,但是B無法知道,既然A已經告訴我不能再發資料了,那我就乖乖聽話不發了;A(女生)不會主動告知B(男生)哈哈,死鎖了!

如何破?用Persist Timer來解鎖!男生B一旦定時器超時,會發一個 Window Probe 訊息給女生A,佔一個位元組,意思是:Are you ready? Do you still love me? 如果A依然很忙,發 window = 0 給男生B,意思是:本姑娘正忙,沒空睬儂!男生B再Restart 自己的定時器,定時器超時再次騷擾女生A,一直會等到A發 window > 0 才結束,本姑娘空閒,歡迎隨時來稿。於是男生欣喜若狂,繼續傳送資料,大段大段的情話…


Scaling Window: 位於TCP option裡,在TCP SYN時傳送scaling window,最大16,即2的16次方;如果對方沒有此option,說明對方不支援,我方也要放棄使用。window size 與scaling window 兩者相乘即得到TCP 的最大真實 window size = 64K* 64K= 4.29G bytes,比如我在國內發到美國的TCP流量,如果最大window size 是1 G 位元組,一梭子就可以打出1G byte,我的1G的尾部資料還沒有發完,我的先鋒領頭位元組已經到達對方,然後對方就選擇確認,對方已經重新整理window size 了,意味著我方不需要停止等待,可以一直這麼傳送下去,這是一種提高TCP傳送速率的一種高效手段。但使用scaling window 有前提的,適合高速、長管道、丟包率極小


TCP建立連線的本質:A和B告知彼此的第一個傳送位元組的初始序列號,建立連線後對每一個傳送的位元組都需要以初始序列號為原點進行編號,需要對方來確認每一個位元組編號都已經成功接收,雙方初始序列號是由OS動態生成的,隨機的值,一般每個TCP session都會有不一樣的初始序列號,佔四個位元組。


滑動視窗( Slide Window):為了便於描述,假定A和B的初始序列號都為0,A發1024位元組的資料給B,B通告給A的window size = 4096。

對於A來說,最初的slide window 左側是自己的原點,即初始序列號0,右側就是 A的初始序列號+ B通告給A的 window size= 4096;

IF
A發出的1024 byte 資料如果沒有得到B確認,A的slide window依然是靜止的。

Else IF
A發出的1024 byte 資料如果得到B確認,B通告的 window = 3072,說明資料還在TCP接收緩衝區,application沒有取走,則A的slide window左側向右的方向移動1024,右側依然靜止是4096 ( 1024 + 3072)。

Else IF
A發出的1024 byte 資料如果得到B確認,B通告的 window = 4096,說明資料被application取走,則A的slide window左側向右的方向移動1024,右側移動到5120 ( 1024 + 4096)。

總結一下,A的slide window 左側是自己發出,並被對方確認的位元組序列號,右側是左側+ B通告window size。


TCP流量控制:TCP三次同步握手建立連線之後,會採用slow start 演算法來快速摸到傳輸路徑頻寬的峰值。為了便於敘述,假定window size 1024 byte = 1 segment,以下全採用segment,slow start 演算法如下:

1)無論對方advertised window size 多大,A第一次只發一個TCP segment,即 congestion window =1,等待對方確認

2)收到確認,發2個 Congestion window = 2, 等待對方確認 

3)收到確認,繼續加倍,發4個…



就這麼指數翻倍,那是不是A可以無窮無盡地增長下去?當然不會,A的傳送速率受到兩個值制約:

一個是對方的advertised window size ,如果此值是8,那A最大一次只能發8個segment,需要等待B的確認,在A等待期間,A到B方向頻寬是閒置的,需要至少等待一個 RTT(Round Trip Time),收到B的確認並且 window >= 1024 ,A才可以繼續傳送。如果網路頻寬無限大,網路沒有丟包,那頻寬的峰值由對方的advertised window size 以及A與B之間RTT來決定。window size 越大,說明對方接收緩衝區越大,A可以最大限度利用頻寬,可以讓位元組流充分佔據鏈路,頻寬閒置率低,傳送速率也就越高;RTT越小,A的等待時間也相應小一些,但它對A傳送速率的影響遠沒有window size來的大。

另外一個制約值就是路徑實時頻寬,A方傳送速率一直翻倍,終於到達了其最大峰值,此時的congestion window 被儲存到slow start threshold,再大就開始丟包了,A方沒有收到被丟棄包的確認,知道可能觸發路徑的擁塞,於是啟動了另外一個法,congestion avoidance, 先將slow start threshold = 1/2 *slow start threshold,congestion window = slow start threshold +1 , 可以看出A方的傳送速率立馬就減到峰值的一半,於是以1/2的峰值速率 線性增長。何謂線性增長?就是收到對方確認,每次對 congestion window + 1,如果出現又丟包了,再將傳送速率減半,然後線上性增長。這就是TCP特點,速率減少時,指數對半下降;速率上升時,slow start 是指數翻倍上升,而congestion avoidance 是線性增長。

總結一下,slow start雖然字面是慢啟動,但是它可以指數翻倍增長,快速上摸到路徑的頻寬極限值,一旦有丟包,指數1/2下降,特點是大起大落,這不利於頻寬的充分利用。

A方啥時採用slow start? 
A_Send_Window < Slow Start Threshold

而congestion avoidance 則是用來克服slow start 的不足,採用小碎步的方式,線性增長逼近頻寬的峰值。

A方啥時採用congestion avoidance?
A_Send_Window > Slow Start Threshold