之前學習了滑動窗口,滑動窗口用來根據接收方的能接收數據的緩存大小來對發送方進行流量控制,從而減少網路負擔,保證網絡的正常運行。但是,在發送端和接收端之間,可能會存在很多中間設備,包括路由器、網關等,這些設備也具有一定的承載數據的上限,也會引起網絡擁塞,造成數據的丟失,造成接收端接受數據的失序。為了解決這個問題,引入了擁塞窗口,即在發送端設置一個窗口結構,根據網絡的擁塞情況,動態調整該窗口大小,發送端只能發送大小小於滑動窗口和擁塞窗口的數據,在發送端設置的這個窗口就是擁塞窗口。
一、超時重傳
我們知道TCP提供的是面向連接的可靠數據傳輸。面向連接就是在交換數據前,在收發兩端要建立連接。可靠主要體現在對於網絡中出現的丟包、包亂序等情況,TCP發送端會根據接收端返回的情況,選擇重傳這些數據。在TCP中,主要通過兩種方式來檢測丟包或亂序的出現,第一種是發送端設置超時時間,第二種是接收端發送重復ACK包。
設置超時時間是指,TCP發送一個報文段時,設置該報文段的超時時間,如果接收端接收到報文段,會給發送端發送一個ACK確認報文,如果發送端在超時時間前未收到對應的ACK報文,發送端則會選擇重發丟失的報文,直到發送端收到非重傳數據為止。設置超時時間的核心問題是如何確定RTO(Retransmission Time Out, 重傳超時時間),有很多關於這個問題的研究和成果。RTO是動態的,會根據網路擁塞情況動態變化。最核心的是,基本上所有的方法都是通過連接的RTT(Round Trip Time, 往返時間)確定RTO。早期的方法是通過每次得到的RTT樣本值重新計算新的TRTO值。公式如下。其中SRTT為每次的迭代更新的RTT,RTT為樣本RTT測量值。ubound、lbound、β為常量。
早期的經典方法沒有考慮到RTT的變化情況,RTT變化情況更能反映當前的擁塞情況,所以Jacobson提出了標准方法。具體公式這里不想討論。同時對於我們如何獲取樣本RTT,也有一些值得探討的問題。我們一般是通過在發送的報文中設置某些標識,在接收端收到並返回ACK時也會帶上這些標識,我們就可測量這段時間為RTT。但是如果在測量RTT時發生超時重傳,如果第一個包發生超時,就會重傳,接着收到了一個ACK確認報文,但這個確認是對那一次發送的確認呢?可能第一次發送的報文,由於網絡擁堵,接收端只是延時收到了,但觸發了超時時間,所以這個ACK有可能就是對第一個報文的確認,這就產生了二義性。也就產生了Karn算法。Karn算法包含兩個部分,一是發生超時重傳時,接受到重傳數據的確認信息時不能更新RTT估計值,這樣就忽略了二義性的問題。二是采用一個退避指數,每當重傳的報文計時器超時時,就讓退避指數加倍,即讓下一次重發時間加倍,直到接受到非重傳數據。
下面來看基於接收端發給發送端的報文,判斷網絡的擁塞,是否出現丟包或包亂序的問題。一般情況是,當接收端發現數據丟失或數據亂序時,接收端選擇發送重復ACK確認報文,向發送端報告數據丟失或亂序。一般來說,小於三個ACK重復報文代表數據亂序,大於等於三個重復ACK代表數據丟失。當發送端觀察到這種情況后,不管超時計時器是否超時,都會選擇重發丟失或亂序的數據。
二、慢啟動和擁塞控制
前面了解了超時重發后,我們開始討論慢啟動和擁塞控制。慢啟動顧名思義,是為了進行流量控制,發送端控制發送數據的大小的動作。初始時,發送端發送一個報文段,等收到接收端對該報文段的確認ACK后,發送端就可以發送2個報文段,當收到對這兩個報文段的確認ACK后,發送端就可以發送4個報文段,一直按照指數增長下去。這種TCP中使用的發送數據的方式為慢啟動(Slow Start)。一般在TCP中,慢啟動和擁塞控制同時使用。
下面介紹擁塞控制,擁塞控制也就是在發送端設置一個窗口結構,窗口大小為cwnd,其根據網絡的擁塞情況動態變化,也就是根據連接的超時重發情況動態變化。發送端只能發送小於或等於擁塞窗口和滑動窗口大小的數據。初始時cwnd為1,ssthreshold(Start Slow Threshold)為65535。當cwnd <= ssthreshold時,執行慢啟動;當cwnd > ssthreshold后,執行擁塞控制。慢啟動前面說了可以傳輸的數據量以指數形式增長。擁塞控制是cwnd線性增長,即要么以1/cwnd,要么以1增長。
1.當出現因超時引起的重傳時,執行:
1.ssthreshold = cwnd / 2
2.cwnd = 1
3.進入慢啟動
2.當出現發送端接收到三個重復的ACK后發生的重傳,執行:
1.cwnd = cwnd / 2
2.ssthreshold = cwnd
3.進入快速恢復算法
快速恢復算法:
1.前面已經更新了cwnd和ssthreshold
2.隨后設置cwnd = cwnd + 3 * MSS
3.重傳數據
4.再每收到一個重復ACK,cwnd = cwnd + 1
5.直到沒收到重復ACK,設置cwnd = ssthreshold