【網絡通信 -- 直播】IM 學習系列 -- 網絡通信協議簡介(TCP 傳輸控制協議)

【網絡通信 -- 直播】IM 學習系列 -- 網絡通信協議簡介(TCP 傳輸控制協議)

【1】TCP 的特點

TCP提供一種面向連接的、可靠的字節流服務;
面向連接意味着 : 兩個使用TCP的應用在彼此交換數據之前必須先建立一個TCP連接,在一個TCP連接中,僅有兩方進行彼此通信
TCP提供可靠性保證的方式:
1. 應用數據被分割成TCP認爲最適合發送的數據塊
2. 當TCP發出一個段後,它啓動一個定時器,等待目的端確認收到這個報文段,如果不能及時收到一個確認,將重發這個報文段;
3. 當TCP收到發自TCP連接另一端的數據,它將發送一個確認,這個確認不是立即發送,通常將推遲幾分之一秒;
4. TCP將保持它首部和數據的檢驗和,這是一個端到端的檢驗和,目的是檢測數據在傳輸過程中的任何變化,如果收到段的檢驗和有差錯,TCP將丟棄這個報文段和不確認收到此報文段(希望發端超時並重發);
5. 既然TCP報文段作爲IP數據報來傳輸,而IP數據報的到達可能會失序,因此TCP報文段的到達也可能會失序,如果必要,TCP將對收到的數據進行重新排序,將收到的數據以正確的順序交給應用層;
6. 既然IP數據報會發生重複,TCP的接收端必須丟棄重複的數據;
7. TCP還能提供流量控制,TCP連接的每一方都有固定大小的緩衝空間,TCP的接收端只允許另一端發送接收端緩衝區所能接納的數據,這將防止較快主機致使較慢主機的緩衝區溢出;

【2】TCP 的部首

1) 16 位端口號 : 告知主機該報文段是來自哪裏(源端口Source Port) 以及傳給哪個上層協議或應用程序 (目的端口 Destination Port) 的,進行 TCP 通信時,客戶端通常使用系統自動選擇的臨時端口號,而服務器則使用知名服務端口號(比如 DNS 協議對應端口 53,HTTP 協議對應 80,這些端口號可在 /etc/services 文件中找到); 
2) 32位序號 : 一次 TCP 通信 (從 TCP 連接建立到斷開) 過程中某一個傳輸方向上的字節流的每個字節的編號,假設主機 A 和主機 B 進行 TCP 通信,A 發送給 B 的第一個 TCP 報文段中,序號值被系統初始化爲某個隨機值 ISN (Initial Sequence Number,初始序號值), 那麼在該傳輸方向上 (從 A 到 B),後續的 TCP 報文段中序號值將被系統設置成 ISN 加上該報文段所攜帶數據的第一個字節在整個字節流中的偏移;例如,某個 TCP 報文段傳送的數據是字節流中的第 1025~2048 字節,那麼該報文段的序號值就是 ISN+1025;另外一個傳輸方向(從 B 到 A)的 TCP 報文段的序號值也具有相同的含義;
3) 32位確認號(acknowledgement number) : 用作對另一方發送來的 TCP 報文段的響應;其值是收到的 TCP 報文段的序號值加 1;假設主機 A 和主機 B 進行 TCP 通信,那麼 A 發送出的 TCP 報文段不僅攜帶自己的序號,而且包含對 B 發送來的 TCP 報文段的確認號;反之,B 發送出的 TCP 報文段也同時攜帶自己的序號和對 A 發送來的報文段的確認號;
4) 4位頭部長度(header length) : 標識該 TCP 頭部有多少個 32bit 字(4字節);因爲4位最大能標識15,所以 TCP 頭部最長是 60 字節;
5) 6位標誌位包含如下幾項 :
URG標誌,表示緊急指針(urgent pointer)是否有效;
ACK標誌,表示確認號是否有效;我們稱攜帶 ACK 標識的 TCP 報文段爲確認報文段
PSH標誌,提示接收端應用程序應該立即從 TCP 接收緩衝區中讀走數據,爲接收後續數據騰出空間(如果應用程序不將接收到的數據讀走,它們就會一直停留在 TCP 接收緩衝區中);
RST標誌,表示要求對方重新建立連接,稱攜帶 RST 標誌的 TCP 報文段爲復位報文段;
SYN標誌,表示請求建立一個連接,稱攜帶 SYN 標誌的 TCP 報文段爲同步報文段;
FIN標誌,表示通知對方本端要關閉連接了,稱攜帶 FIN 標誌的 TCP 報文段爲結束報文段;
6) 16位窗口大小(window size) : 是 TCP 流量控制的一個手段,此處的窗口,指的是接收通告窗口(Receiver Window,RWND),告訴對方本端的 TCP 接收緩衝區還能容納多少字節的數據,這樣對方就可以控制發送數據的速度;
7) 16位校驗和(TCP check sum) : 由發送端填充,接收端對 TCP 報文段執行 CRC 算法以檢驗 TCP 報文段在傳輸過程中是否損壞;注意,這個校驗不僅包括TCP頭部,也包括數據部分,這也是 TCP 可靠傳輸的一個重要保障;
8) 16位緊急指針(urgent pointer) : 是一個正的偏移量,它和序號字段的值相加表示最後一個緊急數據的下一字節的序號;因此,確切地說,這個字段是緊急指針相對當前序號的偏移,不妨稱之爲緊急偏移;TCP 的緊急指針是發送端向接收端發送緊急數據的方法;
9) TCP 頭部選項 : TCP 頭部的最後一個選項字段(options)是可變長的可選信息;

kind=0 是選項表結束選項;
kind=1 是空操作(nop)選項,沒有特殊含義,一般用於將 TCP 選項的總長度填充爲 4 字節的整數倍;
kind=2 是最大報文段長度選項,TCP 連接初始化時,通信雙方使用該選項來協商最大報文段長度(Max Segement Size,MSS),TCP 模塊通常將 MSS 設置爲(MTU-40)字節(減掉的這 40 字節包括 20 字節的 TCP 頭部和 20 字節的 IP 頭部),這樣攜帶 TCP 報文段的 IP 數據報的長度就不會超過 MTU (假設 TCP 頭部和 IP 頭部都不包含選項字段,並且這也是一般情況),從而避免本機發生 IP 分片;對以太網而言,MSS 值是 1460 (1500-40)字節;
kind=3  是窗口擴大因子選項,TCP 連接初始化時,通信雙方使用該選項來協商接收通告窗口的擴大因子;在 TCP 的頭部中,接收通告窗口大小時用 16 位表示的,故最大爲 65535 字節,但實際上 TCP 模塊允許的接收通告窗口大小遠不止這個數 (爲了提高 TCP 通信的吞吐量),窗口擴大因子解決了這個問題;假設 TCP 頭部中的接收通告窗口大小是 N 乘 2 的 M 次方,或者說 N 左移 M 位;注意,M 的取值範圍是 0~14,可以通過修改 /proc/sys/net/ipv4/tcp_window_scaling 內核變量來啓用或關閉窗口擴大因子選項;
kind=5 是 SACK 實際工作的選項,該選項的參數告訴發送方本端已經收到並緩存的不連續的數據塊,從而讓發送端可以據此檢查並重發丟失的數據塊;每個塊邊沿(edge of block) 參數包含一個 4 字節的序號;其中塊左邊沿表示不連續塊的第一個數據的序號,而塊右邊沿則表示不連續塊的最後一個數據的序號的下一個序號;這樣一對參數 (塊左邊沿和塊右邊沿) 之間的數據是沒有收到的;因爲一個塊信息佔用 8 字節,所以 TCP 頭部選項中實際上最多可以包含 4 個這樣的不連續數據塊(考慮選項類型和長度佔用的 2 字節);
kind=8 是時間戳選項,該選項提供了較爲準確的計算通信雙方之間的迴路時間 (Round Trip Time,RTT) 的方法,從而爲 TCP 流量控制提供重要信息,我們可以通過修改 /proc/sys/net/ipv4/tcp_timestamps 內核變量來啓用或關閉時間戳選項;

【3】TCP 狀態轉移與連接的建立、中止

1. 對於建鏈接的3次握手:主要是要初始化 Sequence Number 的初始值,通信的雙方要互相通知對方自己的初始化的 Sequence Number(縮寫爲ISN:Inital Sequence Number),SYN(Synchronize Sequence Numbers)即上圖中的 x 和 y,這個號要作爲以後的數據通信的序號,以保證應用層接收到的數據不會因爲網絡上的傳輸的問題而亂序(TCP會用這個序號來拼接數據);
2. 對於4次揮手:其實是2次,因爲TCP是全雙工的,發送方和接收方都需要 Fin 和 Ack,只不過,有一方是被動的,所以看上去就成了所謂的4次揮手;
3. 關於建連接時SYN超時:如果server端接到了clien發的SYN後回了SYN-ACK後client掉線了,server端沒有收到client回來的ACK,那麼,這個連接處於一箇中間狀態,既沒成功,也沒失敗;於是,server端如果在一定時間內沒有收到的TCP會重發SYN-ACK,在Linux下,默認重試次數爲5次,重試的間隔時間從1s開始每次都翻倍,5次的重試時間間隔爲1s, 2s, 4s, 8s, 16s,總共31s,第5次發出後還要等32s都知道第5次也超時了,所以,總共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 2^6 -1 = 63s,TCP纔會把斷開這個連接;
4. 最大報文段長度(MSS):TCP傳往另一端的最大塊數據的長度,當一個連接建立時,連接的雙方都要通告各自的MSS;

5. 在TCP的狀態圖中,從TIME_WAIT狀態到CLOSED狀態,有一個超時設置,這個超時設置是 2*MSL,爲什麼要這有TIME_WAIT?主要有兩個原因:1)TIME_WAIT確保有足夠的時間讓對端收到了ACK,如果被動關閉的那方沒有收到Ack,就會觸發被動端重發Fin,一來一去正好2個MSL,2)有足夠的時間讓這個連接不會跟後面的連接混在一起【詳見 【Linux網絡編程】TCP連接的分組交換與狀態轉移】;

6. 平靜時間的概念:TCP在重啓動後的MSL秒內不能建立任何連接,問題描述,如果使用處於2MSL等待端口的主機出現故障,它會在MSL秒內重新啓動,並立即使用故障前仍處於2MSL的插口對來建立一個新的連接,若如此則在故障前從這個連接發出而遲到的報文段會被錯誤地當作屬於重啓後新連接的報文段;

難點總結

1. TCP 連接的初始化序列號能否固定

ISN(Inital Sequence Number)和一個假的時鐘綁在一起,這個時鐘會在每4微秒對ISN做加一操作,直到超過2^32,又從0開始,這需要4小時纔會產生ISN的迴繞問題,這幾乎可以保證每個新連接的ISN不會和舊的連接的ISN產生衝突,現在的實現大多是在一個基準值的基礎上進行隨機的;

2. TIME_WAIT 帶來的問題

TIME_WAIT帶來的問題主要源於,一個連接進入TIME_WAIT狀態後需要等待2*MSL(一般是1到4分鐘)的時間才能斷開連接釋放連接佔用的資源,因此會造成以下問題:

  • 1) 作爲服務器,短時間內關閉了大量的Client連接,就會造成服務器上出現大量的TIME_WAIT連接,嚴重消耗着服務器的資源;
  • 2) 作爲客戶端,短時間內大量的短連接,會大量消耗的Client機器的端口,畢竟端口只有65535個,端口被耗盡了,後續將無法在發起新的連接;

3. TIME_WAIT的快速回收和重用

TIME_WAIT的快速回收

linux下開啓TIME_WAIT快速回收需要同時打開tcp_tw_recycle和tcp_timestamps(默認打開)兩選項,Linux下快速回收的時間爲3.5 * RTO(Retransmission Timeout),而一個RTO時間爲200ms至120s;

拒絕新連接需要同時滿足以下三個條件

  • 1)來自同一個對端Peer的TCP包攜帶了時間戳;
  • 2)之前同一臺peer機器(僅僅識別IP地址,因爲連接被快速釋放了,沒了端口信息)的某個TCP數據在MSL秒之內到過本Server;
  • 3)Peer機器新連接的時間戳小於peer機器上次TCP到來時的時間戳,且差值大於重放窗口戳(TCP_PAWS_WINDOW);

特殊情況 : 在一個NAT後面的所有Peer機器在Server看來都是一個機器,NAT後面的那麼多Peer機器的系統時間戳很可能不一致,有些快,有些慢,這樣,在Server關閉了與系統時間戳快的Client的連接後,在這個連接進入快速回收的時候,同一NAT後面的系統時間戳慢的Client向Server發起連接,這就很有可能同時滿足上面的三種情況,造成該連接被Server拒絕掉;

TIME_WAIT的重用

只要滿足下面兩點中的一點,一個TW狀態的四元組(即一個socket連接)可以重新被新到來的SYN連接使用:

  • 1)新連接SYN告知的初始序列號比TIME_WAIT老連接的末序列號大;
  • 2)如果開啓了tcp_timestamps,並且新到來的連接的時間戳比老連接的時間戳大;

注意 :要同時開啓tcp_tw_reuse選項和tcp_timestamps 選項纔可以開啓TIME_WAIT重用;

tcp_tw_reuse 和 SO_REUSEADDR 選項對比
兩者是兩個完全不同的概念,tcp_tw_reuse是內核選項,而SO_REUSEADDR用戶態的選項,使用SO_REUSEADDR是告訴內核,如果端口忙,但TCP狀態位於 TIME_WAIT ,可以重用端口;如果端口忙,而TCP狀態位於其他狀態,重用端口時依舊得到一個錯誤信息, 指明Address already in use”;

【3.1】TCP 的半關閉

TCP的半關閉:TCP提供的連接的一端在結束它的發送後還能接收來自另一端數據的能力;

【3.2】TCP 雙方同時建立連接

【3.3】TCP 雙方同時關閉連接

【4】TCP 的交互數據流

【4.1】經受時延的確認

通常TCP在接收到數據時並不立即發送ACK,相反,它推遲發送,以便將ACK與需要沿該方向發送的數據一起發送(這種現象爲數據捎帶ACK),絕大多數實現採用的時延爲200 ms,也就是說,TCP將以最大200 ms的時延等待是否有數據一起發送;

難點總結

1. TCP的延遲確認機制

按照TCP協議,確認機制是累積的,也就是確認號X的確認指的是所有X之前但不包括X的數據已經收到了;確認號(ACK)本身就是不含數據的分段,因此大量的確認號消耗了大量的帶寬,雖然大多數情況下,ACK還是可以和數據一起捎帶傳輸的,但是如果沒有捎帶傳輸,那麼就只能單獨回來一個ACK,如果這樣的分段太多,網絡的利用率就會下降;延遲的ACK即ACK在收到數據後並不馬上回復,而是延遲一段可以接受的時間,看能不能和接收方要發給發送方的數據一起回去,因爲TCP協議頭中總是包含確認號的,如果能的話,就將數據一起捎帶回去,這樣網絡利用率就提高了;
延遲ACK就算沒有數據捎帶,那麼如果收到了按序的兩個包,那麼只要對第二包做確認即可,這樣也能省去一個ACK消耗;由於TCP協議不對ACK進行ACK的,RFC建議最多等待2個包的積累確認,這樣能夠及時通知對端Peer,我這邊的接收情況;
ACK分段的確認號,是確認按序收到的最後一個字節序,對於亂序到來的TCP分段,接收端會回覆相同的ACK分段,只確認按序到達的最後一個TCP分段,TCP連接的延遲確認時間一般初始化爲最小值40ms,隨後根據連接的重傳超時時間(RTO)、上次收到數據包與本次接收數據包的時間間隔等參數進行不斷調整;

【4.2】Nagle 算法

Nagle 算法要求一個TCP連接上最多隻能有一個未被確認的未完成的小分組,在該分組的確認到達之前不能發送其他的小分組,相反,TCP收集這些少量的分組,並在確認到來時以一個分組的方式發出去,該算法的優越之處在於它是自適應的:確認到達得越快,數據也就發送得越快;

【5】TCP 的成塊數據流

【5.1】快的發送方與慢的接收方

【6】TCP 的流量控制

【6.1】滑動窗口

TCP頭裏有一個字段叫Window,又叫Advertised-Window,這個字段是接收端告訴發送端自己還有多少緩衝區可以接收數據,於是發送端就可以根據這個接收端的處理能力來發送數據,而不會導致接收端處理不過來; 

1. 接收端LastByteRead指向了TCP緩衝區中讀到的位置,NextByteExpected指向的地方是收到的連續包的最後一個位置,LastByteRcved指向的是收到的包的最後一個位置,中間有些數據還沒有到達,存在數據空白區;
2. 發送端的LastByteAcked指向了被接收端Ack過的位置(表示成功發送確認),LastByteSent表示發出去了,但還沒有收到成功確認的Ack,LastByteWritten指向的是上層應用正在寫的地方;
3. 接收端在給發送端回ACK中會彙報自己的AdvertisedWindow = MaxRcvBuffer – LastByteRcvd – 1;
4. 發送方會根據這個窗口來控制發送數據的大小,以保證接收方可以處理;

窗口左右邊沿移動的術語

1. 窗口合攏,窗口左邊沿向右邊沿靠近,這種現象發生在數據被髮送和確認時;
2. 窗口張開,當窗口右邊沿向右移動時將允許發送更多的數據,這種現象發生在另一端的接收進程讀取已經確認的數據並釋放了TCP的接收緩存時;
3. 窗口收縮,窗口右邊沿向左移動;

滑動窗口移動圖示 

 滑動窗滑動後的示意圖

#1 已收到ack確認的數據;
#2 發還沒收到ack的;
#3 在窗口中還沒有發出的(接收方還有空間);
#4 窗口以外的數據(接收方沒空間);

【6.2】接受端控制發送端的圖示

Zero Window

若Window變成0了,TCP發送端就不發數據了,TCP使用Zero Window Probe技術,縮寫爲ZWP,探測窗口大小是否不爲0,即發送端在窗口變成0後,會發ZWP的包給接收方,讓接收方來ack其Window尺寸,一般這個值會設置成3次,每次大約30-60秒,如果3次過後還是0的話,有的TCP實現就會發RST端口鏈接;

Silly Window Syndrome

1. 如果這個問題是由Receiver端引起的,則在receiver端,如果收到的數據導致window size小於某個值,可以直接ack(0)回sender,這樣就把window給關閉了,也阻止了sender再發數據過來,等到receiver端處理了一些數據後windows size 大於等於了MSS,或者 receiver buffer有一半爲空,就可以把window打開讓send 發送數據過來;
2. 如果這個問題是由Sender端引起的,那麼採用 Nagle’s algorithm,這個算法的思路也是延時處理,兩個主要的條件:1)要等到 Window Size>=MSS 或是 Data Size >=MSS,2)收到之前發送數據的ack回包,纔會發數據,否則就是在攢數據;

【7】TCP 的重傳機制

【7.1】TCP 的重傳超時計算

爲了使重傳機制更高效,如果能夠比較準確知道在當前網絡狀況下,一個數據包從發出去到回來的時間 RTT——Round Trip Time,那麼根據該 RTT 就可以方便設置TimeOut——RTO(Retransmission TimeOut);

RTO 的經典算法

1)首先採樣計算RTT值;
2)然後計算平滑的RTT,稱爲Smoothed Round Trip Time (SRTT),SRTT = ( ALPHA*SRTT ) + ((1-ALPHA)*RTT);
3)RTO = min[UBOUND,max[LBOUND,(BETA*SRTT)]];
其中:UBOUND是RTO值的上限;LBOUND是RTO值的下限;ALPHA 平滑因子(e.g., .8 to .9), BETA 延遲方差因子(e.g., 1.3 to 2.0);

改進算法 Jacobson / Karels Algorithm

SRTT = SRTT + α (RTT – SRTT) —— 計算平滑RTT;
DevRTT = (1-β)DevRTT + β(|RTT-SRTT|) ——計算平滑RTT和真實的差距(加權移動平均);
RTO= μ SRTT + ∂ DevRTT
(其中:在Linux下,α = 0.125,β = 0.25, μ = 1,∂ = 4)

每一個TCP連接單一超時定時器的設計的算法規則
1)每一次一個包含數據的包被髮送(包括重發),如果還沒開啓重傳定時器,則開啓它,使得它在RTO秒之後超時(按照當前的RTO值);
2)當接收到一個ACK確認一個新的數據, 如果所有的發出數據都被確認了,關閉重傳定時器;
3)當接收到一個ACK確認一個新的數據,還有數據在傳輸,也就是還有沒被確認的數據,重新啓動重傳定時器,使得它在RTO秒之後超時(按照當前的RTO值);
4)當重傳定時器超時後,依次做下列3件事情:
   4.1)重傳最早的尚未被TCP接收方ACK的數據包
   4.2)重新設置RTO 爲 RTO * 2(“還原定時器”),但是新RTO不應該超過RTO的上限(RTO有個上限值,這個上限值最少爲60s)
   4.3)重啓重傳定時器
上面的建議算法體現了一個原則:沒被確認的包必須可以超時,並且超時的時間不能太長,同時也不要過早重傳;

【7.2】TCP 的重傳機制

Selective Acknowledgment (SACK,選擇確認)機制,SACK是TCP的擴展選項,包括:

  • 1)SACK允許選項(Kind=4,Length=2,選項只允許在有SYN標誌的TCP包中);
  • 2)SACK信息選項(Kind=5,Length);

D-SACK 利用第一塊SACK數據中描述重複接收的不連續數據塊的序列號參數,其他SACK數據則描述其他正常接收到的不連續數據,這樣發送方利用第一塊SACK,可以發現數據段被網絡複製、錯誤重傳、ACK丟失引起的重傳、重傳超時等異常的網絡狀況,使得發送端能更好調整自己的重傳策略;
D-SACK 的優點:

  • 1)發送端可以判斷出,是發包丟失了,還是接收端的ACK丟失了;(發送方,重傳了一個包,發現並沒有D-SACK那個包,那麼就是發送的數據包丟了;否則就是接收端的ACK丟了,或者是發送的包延遲到達了);
  • 2)發送端可以判斷自己的RTO是不是有點小了,導致過早重傳(如果收到比較多的D-SACK就該懷疑是RTO小了);
  • 3)發送端可以判斷自己的數據包是不是被複制了(如果明明沒有重傳該數據包,但是收到該數據包的D-SACK);
  • 4)發送端可以判斷目前網絡上是不是出現了有些包被delay了,也就是出現先發的包卻後到了;

【8】TCP 擁塞的處理

【8.1】網絡阻塞的原因

對於TCP,端到端的流量控制必然會導致網絡擁堵,這是因爲TCP只看到對端的接收空間的大小,而無法知道鏈路上的容量,只要雙方的處理能力很強,那麼就可以以很大的速率發包,於是鏈路很快出現擁堵,進而引起大量的丟包,丟包又引發發送端的重傳風暴,進一步加劇鏈路的擁塞;
鏈路上的轉發節點,例如路由器,再好的路由器只要接入網絡,總是會拉低網絡的總帶寬,如果在路由器節點上出現處理瓶頸,那麼就很容易出現擁塞;

【8.2】冗餘ACK的判定條件(對於SACK,那麼根據SACK的一些信息來進一步判斷)
[1] 接收ACK的那端已經發出了一些還沒被ACK的數據包
[2] 該ACK沒有捎帶data
[3] 該ACK既不是SYN包的ACK也不是FIN包的ACK
[4] 該ACK的確認號等於接收ACK那端已經收到的ACK的最大確認號
[5] 該ACK通知的窗口等於接收該ACK的那端上一個收到的ACK的窗口

【8.3】Reno 算法包含4個部分
[1]慢熱啓動算法 – Slow Start;
[2]擁塞避免算法 – Congestion Avoidance;
[3]快速重傳 - Fast Retransimit;
[4]快速恢復算法 – Fast Recovery;

【8.3.1】慢啓動

慢啓動算法流程

  • 1)連接建好先初始化cwnd = N,表明可以傳N個MSS大小的數據;
  • 2)每當收到一個ACK,cwnd++; 呈線性上升;
  • 3)每當過了一個RTT,cwnd = cwnd*2; 呈指數讓升;
  • 4)ssthresh(slow start threshold)是一個上限,當cwnd >= ssthresh時,就會進入“擁塞避免算法”;

根據 RFC5681,如果MSS > 2190 bytes,則N = 2; 如果MSS < 1095 bytes,則N = 4; 如果2190 bytes >= MSS >= 1095 bytes,則N = 3; 一篇Google的論文《An Argument for Increasing TCP’s Initial Congestion Window》建議把cwnd 初始化成了 10個MSS,Linux 3.0後採用了這篇論文的建議。

【8.3.2】擁塞避免

擁塞避免算法流程

  • 1)收到一個ACK時,cwnd = cwnd + 1/cwnd
  • 2)當每過一個RTT時,cwnd = cwnd + 1

【8.3.3】擁塞發生

TCP是看不到網絡的整體狀況的,那麼TCP認爲網絡擁塞的主要依據是它重傳了報文段;

擁塞發生處理流程

1)出現RTO超時,重傳數據包,這種情況下,TCP就認爲出現擁塞的可能性就很大:

  • sshthresh =  cwnd /2
  • cwnd 重置爲 1
  • 進入慢啓動過程

2)在RTO超時前,收到3個duplicate ACK進行重傳數據包,這種情況下,收到3個冗餘ACK後說明確實有中間的分段丟失,然而後面的分段確實到達了接收端,因爲這樣纔會發送冗餘ACK,這一般是路由器故障或者輕度擁塞或者其它不太嚴重的原因引起的,因此此時擁塞窗口縮小的幅度就不能太大,此時進入快速重傳;

【8.3.4】快速重傳

  • 1) 調整門限ssthresh的值爲當前cwnd值的1/2;
  • 2) 將cwnd值設置爲新的ssthresh的值;
  • 3) 重新進入擁塞避免階段;

在快速重傳的時候,一般網絡只是輕微擁堵,在進入擁塞避免後,cwnd恢復的比較慢,針對這個,“快速恢復”算法被添加進來,當收到3個冗餘ACK時,TCP最後的[3]步驟進入的不是擁塞避免階段,而是快速恢復階段;

【8.3.5】快速恢復

快速恢復算法流程

  • cwnd = sshthresh  + 3 * MSS (3的意思是確認有3個重傳Duplicated ACKs指定的數據包被收到了)
  • 如果再收到 duplicated Acks,那麼cwnd = cwnd +1
  • 如果收到了新的Ack,那麼,cwnd = 快速重傳階段1的 sshthresh 值,然後就進入擁塞避免的算法

New Reno算法,該算法是在沒有SACK的支持下改進Fast Recovery算法(SACK改變TCP的確認機制,把亂序等信息會全部告訴對方,SACK本身攜帶的信息就可以使得發送方有足夠的信息來知道需要重傳哪些包,而不需要重傳哪些包)

  • 1)發送端收到3個冗餘ACK後,重傳冗餘ACK指示可能丟失的那個包segment1,如果segment1的ACK通告接收端已經收到發送端的全部已經發出的數據,那麼就是隻丟失一個包,如果沒有,那麼就是有多個包丟失了;
  • 2)發送端根據segment1的ACK判斷出有多個包丟失,那麼發送端繼續重傳窗口內未被ACK的第一個包,直到sliding window內發出去的包全被ACK了,才真正退出Fast Recovery階段;

 

參考致謝
本博客爲博主的學習實踐總結,並參考了衆多博主的博文,在此表示感謝,博主若有不足之處,請批評指正。

【1】TCP/IP詳解 卷1:協議

【2】[通俗易懂]深入理解TCP協議(上):理論基礎

【3】[通俗易懂]深入理解TCP協議(下):RTT、滑動窗口、擁塞處理

【4】不爲人知的網絡編程(一):淺析TCP協議中的疑難雜症(上篇)

【5】不爲人知的網絡編程(二):淺析TCP協議中的疑難雜症(下篇)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章