網絡擁塞控制算法:
TCP的擁塞控制主要原理依賴于一個擁塞視窗來控制,在之前我們還讨論過TCP還有一個對端通告的接收視窗用于流量控制。視窗值得大小就代表能夠發送出去的但還沒有接收到ACK的最大資料封包段,顯然視窗越大那麼資料發送的速度也就越快,但是也有越可能使得網絡出現擁塞,如果視窗值為1,那麼就簡化為一個停等協定,每發送一個資料,都要等到對方的确認才能發送第二個資料包,顯然資料傳輸效率低下。TCP的擁塞控制算法就是要在這兩者之間權衡,選取最好的cwnd值,進而使得網絡吞吐量最大化且不産生擁塞。
由于需要考慮擁塞控制和流量控制兩個方面的内容,是以TCP得真正的發送視窗=min(rwnd,cwnd)。但是rwnd是由對端确定的,網絡環境對其沒有影響,是以在考慮擁塞控制的時候我們一般不考慮rwnd的值,我們暫時隻讨論如何确定cwnd值的大小。關于cwnd的機關,在TCP中是以位元組來做機關的,我們假設TCP每次傳輸都是按照MSS大小來發送資料的,是以你可以認為cwnd按照資料包個數來做機關也可以了解,是以有時我們說cwnd增加1也就是相當于位元組數增加1個MSS大小。
1、慢啟動
最初的TCP在連接配接建立成功後會向網絡中發送大量的資料包,這樣很容易導緻網絡中路由器緩存空間耗盡,進而發生擁塞。是以建立立的連接配接不能夠一開始就大量發送資料包,而隻能根據網絡情況逐漸增加每次發送的資料量,以避免上述現象的發生。具體來說,當建立連接配接時,cwnd初始化為1個最大封包段(MSS)大小,發送端開始按照擁塞視窗大小發送資料,每當有一個封包段被确認,cwnd就增加1個MSS大小。這樣cwnd的值就随着網絡往返時間(Round Trip Time,RTT)呈指數級增長,事實上,慢啟動的速度一點也不慢,隻是它的起點比較低一點而已。我們可以簡單計算下:
開始 —> cwnd = 1
經過1個RTT後 —> cwnd = 2*1 = 2
經過2個RTT後 —> cwnd = 2*2= 4
經過3個RTT後 —> cwnd = 4*2 = 8
上面讨論的兩個機制都是沒有檢測到擁塞的情況下的行為,那麼當發現擁塞了cwnd又該怎樣去調整呢?
首先來看TCP是如何确定網絡進入了擁塞狀态的,TCP認為網絡擁塞的主要依據是它重傳了一個封包段。上面提到過,TCP對每一個封包段都有一個定時器,稱為重傳定時器(RTO),當RTO逾時且還沒有得到資料确認,那麼TCP就會對該封包段進行重傳,當發生逾時時,那麼出現擁塞的可能性就很大,某個封包段可能在網絡中某處丢失,并且後續的封包段也沒有了消息,在這種情況下,TCP反應比較“強烈”:
1.把ssthresh降低為cwnd值的一半
2.把cwnd重新設定為1
3.重新進入慢啟動過程。
後來的“快速恢複”算法是在上述的“快速重傳”算法後添加的,當收到3個重複ACK時,TCP最後進入的不是擁塞避免階段,而是快速恢複階段。快速重傳和快速恢複算法一般同時使用。快速恢複的思想是“資料包守恒”原則,即同一個時刻在網絡中的資料包數量是恒定的,隻有當“老”資料包離開了網絡後,才能向網絡中發送一個“新”的資料包,如果發送方收到一個重複的ACK,那麼根據TCP的ACK機制就表明有一個資料包離開了網絡,于是cwnd加1。如果能夠嚴格按照該原則那麼網絡中很少會發生擁塞,事實上擁塞控制的目的也就在修正違反該原則的地方。
具體來說快速恢複的主要步驟是:
1.當收到3個重複ACK時,把ssthresh設定為cwnd的一半,把cwnd設定為ssthresh的值加3,然後重傳丢失的封包段,加3的原因是因為收到3個重複的ACK,表明有3個“老”的資料包離開了網絡。
2.再收到重複的ACK時,擁塞視窗增加1。
3.當收到新的資料包的ACK時,把cwnd設定為第一步中的ssthresh的值。原因是因為該ACK确認了新的資料,說明從重複ACK時的資料都已收到,該恢複過程已經結束,可以回到恢複之前的狀态了,也即再次進入擁塞避免狀态。
快速重傳算法首次出現在4.3BSD的Tahoe版本,快速恢複首次出現在4.3BSD的Reno版本,也稱之為Reno版的TCP擁塞控制算法。
可以看出Reno的快速重傳算法是針對一個包的重傳情況的,然而在實際中,一個重傳逾時可能導緻許多的資料包的重傳,是以當多個資料包從一個資料視窗中丢失時并且觸發快速重傳和快速恢複算法時,問題就産生了。是以NewReno出現了,它在Reno快速恢複的基礎上稍加了修改,可以恢複一個視窗内多個包丢失的情況。具體來講就是:Reno在收到一個新的資料的ACK時就退出了快速恢複狀态了,而NewReno需要收到該視窗内所有資料包的确認後才會退出快速恢複狀态,進而更一步提高吞吐量。
SACK就是改變TCP的确認機制,最初的TCP隻确認目前已連續收到的資料,SACK則把亂序等資訊會全部告訴對方,進而減少資料發送方重傳的盲目性。比如說序号1,2,3,5,7的資料收到了,那麼普通的ACK隻會确認序列号4,而SACK會把目前的5,7已經收到的資訊在SACK選項裡面告知對端,進而提高性能,當使用SACK的時候,NewReno算法可以不使用,因為SACK本身攜帶的資訊就可以使得發送方有足夠的資訊來知道需要重傳哪些包,而不需要重傳哪些包。