TCP系列42—擁塞控制—5、Linux中的慢啟動和擁塞避免(二)


在本篇中我們繼續上一篇文章wireshark的示例講解,上一篇介紹了一個綜合示例后,本篇介紹一些簡單的示例,在讀本篇前建議先把上一篇讀完,為了節省篇幅,本篇只針對一些特殊的場景點報文進行講解,不會像上一篇一樣對每個報文都進行講解並隨報文更新相關狀態變量的值了。

一、wireshark示例

本篇示例的TCP測試仍然設置初始擁塞窗口為3,並關閉TSO、GSO等功能。同時設置wireshark使其不在info列顯示TSopt的信息。

  
  
  
          
  1. ******@Inspiron:~$ sudo ip route add local 127.0.0.2 dev lo congctl reno initcwnd 3 #請參考本系列destination metric文章
  2. ******@Inspiron:~$ ip route show table all | grep 127.0.0.2
  3. local 127.0.0.2 dev lo  table local  scope host  initcwnd 3 congctl reno
  4. ******@Inspiron:~$ sudo ethtool -K lo tso off gso off  #關閉tso gso以方便觀察cwnd變化  
1、 慢啟動與延遲ACK

client與server端建立連接后,先發送一個請求報文No4,server端則立即回復ACK確認包。server端在與client建立連接后休眠1000ms,然后每隔5ms發送50bytes的數據包,總共發送14次。client端則每收到兩個報文的時候回復一個ACK確認包。最終TCP流如下圖所示,圖中in_flight列是wireshark對於目前還在傳輸中的數據量的估計,在這種簡單場景下wireshark的估計與linux是一致的,可以作為參考

No9-No13:No9報文回復了No6和No7兩個報文,server端收到No9報文后,直接更新cwn=cwnd+2=5,此時in_flight=packets_out=1,因此可以額外發出四個新的數據包,即No10-No13。從wireshark中也可以看到發送完No13后in_flight為250,正好是5個數據包的大小。

No14-No18:與No9-No13類似不再重復分析

No19-No22:server端收到No19后,只有150bytes三個數據包等待發送,因此最終只發出了No20-No22三個數據包。

2、慢啟動與ABC、stretched ACK、ACK Compression

ABC(Appropriate Byte Count):是指接收端對於接收到的一個TCP報文,分段反饋多個有效的ACK確認包,如果發送端收到每個ACK確認包后都會更新cwnd,那么在慢啟動階段可能會導致cwnd增長異常迅速,而且超過鏈路的承載能力,最終降低TCP的性能。這種手段也稱呼為ABC攻擊。

stretched ACK:我們之前介紹延遲ACK時候介紹過,當以SMSS發送報文的時候,協議規定延遲ACK不能超過兩個full-sized報文。但是如果接收端收到的ACK確認包中ack number確認了三個或者以上的full-sized的報文的時候,這個ACK就叫做stretched ACK。stretached ACK原因有多種,最常見的就是ACK報文丟失。

ACK compression:由於中間鏈路的緩存以及和其他TCP連接一起共享緩存等原因,可能會導致ACK報文成堆到達發送端。這種場景我們就稱呼為ACK壓縮。

對於ACK compression場景,reno擁塞控制就是逐個處理每個ACK報文,這樣就會導致擁塞窗口突然增大,發送端突然發出大量的TCP報文,這種突然發出大量數據的行為我們稱呼為burst,影響網絡平穩。另外一方面ACK compression還會影響RTT估計,之前我們介紹過有些擁塞控制算法基於時延來來估計網絡擁塞情況,因此 ACK compresion還會影響這類基於時延的擁塞控制算法的性能。

在這里我們僅演示一下linux慢啟動對於ABC和stretched ACK的處理場景,擁塞避免階段的處理與慢啟動類似不在示例,如下圖所示我高亮標出了一些ACK報文

No12:可以看到No12相對於No9報文Ack number一共反饋確認了三個數據包,這個就是我們說的stretched ACK。可以看到此處linux對於stretched ACK的處理上,直接更新了cwnd=cwnd+3,然后一共發出了No13-No18六個數據包。

No19-No21:可以看到No19相對於No12,ack number只反饋確認了30bytes的數據,並沒有完整反饋No11這個數據包,No20同樣也沒有完整反饋No11這個數據包,linux在收到這兩個ACK報文的時候,則會判斷是否完整反饋了之前發出的No11數據包,當發現沒有完整反饋No11數據包的時候並不會更新cwnd。直到linux收到No21這個數據包后,發現之前的No11這個數據包已經被完整反饋了,因此更新cwnd=cwnd+1,發出兩個數據包。

3、多個數據包RTO超時

在之前的綜合示例中我們看到過,一次RTO超時觸發重傳后,同一個報文的多次RTO超時並不會繼續更新ssthresh,而隨后的recovery point之前的慢啟動重傳也不會更新ssthresh。但是在recovery point之前的多個數據包觸發的RTO超時則會重新更新ssthresh。一定要記得是不同TCP報文的RTO超時才會更新ssthresh,而linux只有一個RTO定時器。要分清慢啟動重傳和RTO超時重傳的區別。

這個示例與綜合示例非常類似,不同點在於這次RTO超時的是Seq為451的報文,而且No43對應的報文發生了兩次RTO超時。下面總結一下這個示例相對於綜合示例的幾個注意點

No35:可以看到這個loss probe報文是個重傳包,而之前綜合示例中,loss probe報文是一個未發送過的報文,這里一定要分清,No35的重傳是TLP超時觸發的,而不是RTO超時觸發。看不懂的前翻TLP的文章。

No43、No47、No48:No43是一個慢啟動重傳,並不是RTO超時觸發的,因此不會更新ssthresh,而No47則是No43的RTO超時重傳,因此會更新ssthresh = max(cwnd/2, 2),實際上在RTO超時發出No47之前,ssthresh=6,cwnd=4,而RTO超時發出No47報文后,則更新ssthresh=2,cwnd=1。No48同樣是RTO超時重傳,但是No48和No47是同一個數據包的多次連續RTO超時重傳因此不會更新ssthresh。另外這里可以看到wireshark估計的in_flight的大小與linux的差異,例如在RTO超時重傳No35后,linux計算的in_flight為1,大小實際為50bytes。之前我們已經介紹過多個linux和wireshark對數據包理解上的差異了。









免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM