TCP采用個各種辦法來減少流量的傳輸量以及信道的利用率。
延時確認-減少傳輸數量
TCP允許延遲一會再發送ACK,這樣可以將ACK和相同方向的數據結合起來進行發送,從而降低ACK的數量,在一定程度上減輕網絡負載。
圖中通過延遲ACK減少了一個ACK的傳輸數量
也叫稍帶確認(piggybacking)。一般處理請求並產生應答的時間小於200ms時發生。如果耗時比較長,一般是先確認后應答(發數據)
滑動窗口
滑動窗口的工作原理在之前已經做了詳細的介紹,在滑動窗口中有一個很重要的概念:窗口大小。問題來了,TCP是如何設置窗口大小的呢?在介紹之前先看一下TCP的緩存結構。
對於TCP來說接收和發送方都維護着一個緩存。看上圖會發現個問題:如果寫進程寫的速度大於讀進程的讀取速度,這樣會導致接收緩存溢出,最終導致發送失敗。所以引入了窗口大小的概念,接收方通過ACK來實時告知發送方自己剩余緩存的大小(即自己還能接收多少數據),這個大小就是滑動窗口的大小,在傳輸的過程中,每一次ack接收方會根據自己的緩存大小動態的調整窗口的大小,下圖詳細展示了這一過程。
0窗口(Zero Window)
上面滑動窗口的展示中最終發送窗口變成了0.
當發送方收到窗口為0的參數后,便不再發送數據給接收方,這個時候接收方進程一直在讀取數據,最終接收方的TCP緩存會清空,有空間接收數據,這個時候接收方會通過一個ack通知發送方窗口的大小,但是這個ack有可能會丟失(ack沒有重傳功能),發送方因為窗口時0,一直沒有發送數據,所以無法得知最新的窗口大小,通信雙方都進入了一直等待狀態。
為了解決這個問題,TCP為每一個鏈接設計了一個持續計數器(persistence timer),當窗口大小為0時,就會啟動這個計數器,當計數器到期后會發送一個零窗口(zero window)探測報文段(一個字節),接收方通過確認這個探測報文時可以告知發送方最新的窗口大小。(TCP規定,就算窗口為0,也要接收零窗口探測報文)。下圖展示了這種case。
糊塗窗口綜合征(Silly Window Syndrome-SWS)
通俗來解釋這個場景就是一架可以坐500人的飛機只拉1個乘客和5個機組人員,資源極大的浪費。
以太網最大傳輸單元(MTU:max transmission unit)為1500字節,TCP+IP的頭部字段為40字節,所以TCP的最大報文(MSS:Max segment size)長度為1460字節。當傳遞的報文的大小遠遠小於1460。尤其小於頭部40的時候,就出現了上面說的一個大飛機拉一個人的情況,其中MTU為飛機的容量(500),機組乘員為TCP/IP頭部(5),傳輸報文為乘客(1)。
發送端引起的SWS
發送窗口的size大於MSS,但是需要發送的數據遠遠小於MSS,這種情況的解決采用nagle算法:
if there is new data to send #有數據要發送
# 發送窗口緩沖區和隊列數據 >=mss,隊列數據(available data)為原有的隊列數據加上新到來的數據
# 也就是說緩沖區數據超過mss大小,nagle算法盡可能發送足夠大的數據包
if the window size >= MSS and available data is >= MSS
send complete MSS segment now # 立即發送
else
if there is unconfirmed data still in the pipe # 前一次發送的包沒有收到ack
# 將該包數據放入隊列中,直到收到一個ack再發送緩沖區數據
enqueue data in the buffer until an acknowledge is received
else
send data immediately # 立即發送
end if
end if
end if
else里面有小塊數據(小於MSS)時候,解釋下:這個時候需要看是否還有已經發送待確認的數據,如果有則等着先不發,等於先在發送端攢數據。某種程度上又變成了停等協議,如果ACK返回的比較慢,小數據等待的時間就會比較長,最終會影響性能。有些情況並不適用nagle算法,比如人機交互的游戲等,可以禁用nagle算法。
接收端引起的SWS
對於接收端來說,如果接收緩存的窗口大小小於MSS或者某個給定的值后,直接ack 接收窗口的size為0。這樣當發送方收到接收窗口為0時,則停止發送數據進行等待,在此期間,接收端的應用一直在讀取數據,這樣接收窗口會慢慢變大,最終通過發送方的0窗口探測請求告知發送方最新的窗口大小,從而再次開始數據傳輸。
以上介紹了TCP流量控制的一個整體過程,全文完。