擁塞控制不一樣於流量控制,擁塞控制是在擁塞發生時,發送方根據必定的反饋,主動調節本身的發送速率,以防止擁塞惡化的行爲。算法
路由器是網絡中的關鍵組件,其內部有必定量的緩衝區,用於緩存來不及轉發(forward,或稱transmit)出去的packet。若是將網絡當作一個總體,那麼一個網絡的鏈路容量(link capacity)就取決於網絡中的路由器的緩衝區大小。鏈路容量越大,整個網絡可容納的outstanding(即在網絡中propagate)的packet就越多。緩存
當網絡中的packet較少時,每一個packet進入路由器,會較快被轉發出去,幾乎沒有延遲。而當網絡中的packet過多時,路由器來不及轉發,大量的packet就會在路由器中排隊,等待轉發(FIFO),此時packet就有較長的延遲,當緩衝區滿時,後進來的packet甚至會被丟棄(lost packet)。這就是擁塞現象(congestion)。網絡
由TCP的Acknowledgement機制可知,receiver期待能按序地(in sequence)接受下一個packet。當out-of-order packet(亂序包)發生時,receiver(雖然會緩存亂序包)不會將錯就錯地回覆亂序的包,而是堅持請求以前期待的包。receiver會在對應的ACK中指明未接受的packet中序號最小的一個(也就是一直期待的packet),而不是與錯誤的packet的相鄰的下一個。這樣以來,receiver每收到一個亂序包,都會發送一個與上一個ACK內容相同的ACK,直到收到所期待的packet爲止,這就造成了重複ACK(duplicate ACK)。spa
重複ACK是網絡擁塞狀況的一個體現,當重複ACK出現時,說明出現了亂序包或者丟包,而亂序包和丟包又是網絡擁塞所致使的,所以重複ACK越頻繁,網絡擁塞越嚴重。orm
在對網絡擁塞的影響程度上,亂序包和丟包是不一樣的。前者僅僅意味着packet不是按序到達receiver,被跳過的packet極可能會在後面陸續到達,然後者意味着packet不會再到達了,由於已經在網絡上被丟棄了。ci
能夠看出,對於亂序包,sender是不須要作任何處理的,而對於丟包,sender須要負責重傳丟失的packet。sender爲了有針對性地處理兩種狀況,首先要採起必定的機制區分兩種狀況。統計發現,亂序包形成的重複ACK通常都是1~2個,而丟包形成的重複ACK則是3個及以上。所以,sender能夠根據收到的重複ACK的個數,判斷是否發生了亂序包或者丟包。路由
TCP可靠傳輸的核心是重傳機制。當一個packet沒有被receiver接收到時,sender須要重傳該packet,直到receiver接收到packet爲止。it
觸發重傳有兩種狀況,且都與ACK有關。最經典的觸發重傳的狀況是超時。當sender發送一個packet以後,即開啓timer,等待對應的ACK,若是在timer結束前收到ACK,則認爲paceket發送成功,不然認爲發送失敗,繼而進行重傳。能夠看出timer的超時時間(RTO, Retransmission Timeout)是觸發重傳的關鍵,那麼sender是如何合理地設置RTO的呢?很顯然,RTO是與當前網絡環境相關的一個變量,須要實時計算獲得。聯想到RTT,能夠猜想RTO與RTT存在必定的聯繫,事實上RTO就是基於SRTT(smoothed RTT)計算獲得的,二者存在線性關係。每當發送一個新的packet時,timer就被設置爲當前RTO,以後每隔500ms更新一次,當收到相應的ACK時,timer就被中止,不然直到timer遞減到0並觸發重傳。計算RTO的具體算法參考RFC6298。io
Congestion Control不一樣於Flow Control,Congestion Control是在擁塞發生時,sender根據必定的反饋,主動調節本身的發送速率,以防止擁塞惡化的行爲。ast
sender會根據兩種現象探測擁塞的發生:
若是收到來自receiver的3個重複ACK(算上原始ACK,實際上有4個相同的ACK),則認爲發生了擁塞,而且知道ACK中指明的即爲lost packet。
若是ACK超時(超過RTO仍未收到ACK),也認爲發生了擁塞,而且這種狀況說明擁塞更加嚴重(由於連重複ACK都沒收到)。
TCP引入了兩個變量:rwnd和cwnd,用於擁塞控制。rwnd是receiver的接收窗口(receive window)的大小,也是它所建議的sender的發送窗口的大小,此變量最初目的是用於flow control中,協調雙方的發送/接收速率。cwnd是sender的擁塞窗口的大小,sender所能發送的packet被限制在該窗口內,考慮到Flow Control裏的發送窗口,sender最終的發送窗口被定義爲min{cwnd, rwnd},所以LastByteSent – LastByteAcked <= min{cwnd, rwnd}.
cwnd和rwnd的單位是MSS(maximum segment size),表示TCP容許的最大單個報文的長度,不包括TCP頭部信息。
能夠粗略地認爲發送窗口的大小就是cwnd。TCP通常在一個 RTT內,將發送窗口裏的packet所有發送出去,所以發送速率爲cwnd/RTT。所以,sender調節本身的發送速率實際上就是調整cwnd的大小。