天天看點

(十二)深入淺出TCPIP之Nagle算法

 TCP的資料流大緻可以分為兩類,互動資料流與成塊的資料流。互動資料流就是發送控制指令的資料流,比如relogin,telnet,ftp指令等等;成塊資料流是用來發送資料的包,網絡上大部分的TCP包都是這種包。

很明顯,TCP在傳輸這兩種類型的包時的效率是不一樣的,是以為了提高TCP的傳輸效率,應該對這兩種類型的包采用不同的算法。

總之,TCP的傳輸原則是盡量減少小分組傳輸的數量。 

Nagle算法分析

Nagle算法主要用來預防小分組的産生。在廣域網上,大量TCP小分組極有可能造成網絡的擁塞。

Nagle時針對每一個TCP連接配接的。它要求一個TCP連接配接上最多隻能有一個未被确認的小分組。在改分組的确認到達之前不能發送其他小分組。TCP會搜集這些小的分組,然後在之前小分組的确認到達後将剛才搜集的小分組合并發送出去。

有時候我們必須要關閉Nagle算法,特别是在一些對時延要求較高的互動式操作環境中,所有的小分組必須盡快發送出去。

我們可以通過程式設計取消Nagle算法,利用TCP_NODELAY選項來關閉Nagle算法。

來看看Nagle大緻的邏輯:

if 有資料要發送:


if 可用視窗大小 >= MSS and 可發送的資料 >= MSS:


立刻發送MSS大小的資料


else :


if 有未确認的資料:


将資料放入緩存等待接收ACK


else:


立刻發送資料      

通過上面的邏輯可以看到,如果是大量資料需要發送,大部分情況都可以填滿一個MSS(也就不存在"小包"的問題),是不需要等待一個未确認包的。

Nagle算法是時代的産物,因為當時網絡帶寬有限。而目前的區域網路、廣域網的帶寬則寬裕得多,是以目前的TCP/IP協定棧預設将Nagle算法關閉 

long noDelay = 1;
 
setsockopt(m_hSocket, IPPROTO_TCP, TCP_NODELAY,(LPSTR)&noDelay, sizeof(long));      

noDelay為1打開nagle算法,為0禁用nagle算法。

延遲确認機制(TCP delayed acknowledgment)

wiki的解釋https://en.wikipedia.org/wiki/TCP_delayed_acknowledgment

1989 RFC 1122定義,全名Delayed Acknowledgment,簡稱延遲ACK,翻譯為延遲确認。 

與Nagle算法一樣,延遲ACK的目的也是為了減少網絡中傳輸大量的小封包數,但該封包數是針對ACK封包的。 

一個來自發送端的封包到達接收端,TCP會延遲ACK的發送,希望應用程式會對剛剛收到的資料進行應答,這樣就可以用新資料将ACK捎帶過去。

當Nagle算法遇到Delayed ACK

在一個有資料傳輸的TCP連接配接中,如果隻有資料發送方啟用Nagle算法,在其連續發送多個小封包時,Nagle算法機制會減少網絡中的小封包數量。這就意味着,同樣傳輸相同大小的應用資料,在網絡上的封包個數卻不同。 

舉個例子,發送端需要連續發送5個寫操作(應用程式将資料寫入到緩沖池的動作)的小封包,首先發送第一個,由于Nagle算法的作用,在未收到第一個封包确認前,發送端在等待寫操作的同時進行讀操作,接收端并未啟用延遲确認(視TCP delay ACK時間為0),盡管剛收到該封包就發出确認,但由于網絡延時的原因,在收集齊另外4個小封包後,發送方才收到了第一個封包的ACK,則後面的4個封包會一起發送出去(大小未超過MSS),接收端再次ACK。

在上述發送5個小封包的過程中,隻用了4個封包就實作了。但如果發送端未啟用Nagle算法,完成整個過程則至少需要8個封包或10個封包才能實作,這裡接收端未啟用延遲确認,如下圖所示。啟用Nagle算法和未啟用Nagle算法的場景中,從完成資料發送的時間來看,未啟用Nagle算法的方式花費的時間會更長一些,如下圖所示。這裡基本看到了Nagle算法的好處了。

還是上述資料傳輸場景,發送端未啟用Nagle算法,但接收端延遲确認預設時間為200ms,來看看這時的情況。RFC 1122規定,Delayed ACK對單個的小封包可以延長确認的時間,但不允許有兩個連續的小封包不被确認。是以,當發送端連續發送兩個封包後,接收端必須給予确認。這時的資料傳輸情況如下圖,隻有當第5個封包到達後,接收端由于延遲确認機制,會導緻200ms的延時存在。

接下來看看,當Nagle算法遇到Delayed ACK時會是什麼情況。按照常理推斷,兩種深思熟慮的功能設計,應該是1+1>2的效果。具體如何,還是請事實說話。

先繼續看上面的假設場景,該場景要求發送端向接收端發送5個連續的寫操作資料,但網絡延時較大,同時發送端啟用Nagle算法,接收端Delayed ACK預設為200ms。 

發送方先發出一個小封包,接收端收到後,由于延遲确認的機制,等待發送方的下一個封包到達。而發送方由于Nagle算法機制,在未接收到第一個封包的确認前,不會發送已讀取到的封包。 在這種場景下,暫不考慮應用處理時間,完成整個資料傳輸所需時間為2RTT+400ms,貌似情況不是特别糟糕。

如果上述其他條件不變,發送方應用寫操作延時稍微變大,或發送端的應用操作延時稍大,我們再看看,完成這個操作的延時情況。 

發送方先發出一個小封包,接收端收到後,由于延遲确認的機制,等待發送方的下一個封包到達。由于發送方應用資料寫操作延時較大,在經過RTT+200ms後,讀取到了下一個需要發送的内容,此時接收到了第一個封包的确認,而網絡中未有沒被确認的封包,發送方需要再将第二個小封包發送出去,以此類推,直到最後一個小封包被發送,且接收到該封包的确認,此時整個資料傳輸過程完成。 

在這種情景下,完成整個資料傳輸所需時間則為5RTT+5*200ms,明顯增大了不少。如果相同情境下,有成千上萬的小封包發送,則整體使用時間相當可觀了。

在實際情況下,如果發送方程式做了一系列的寫、寫、讀操作的現象,這樣的操作都會觸發Nagle和延遲ACK算法之間的互動作用,應該盡量避免。

應用場景: