1. 程式人生 > >lwip中Nagle 演算法實現

lwip中Nagle 演算法實現

何謂愚笨視窗綜合症:

愚笨視窗綜合症有2種變現形式:接收方愚笨視窗綜合症和傳送方愚笨視窗綜合症。
考慮以下情形。當接收方的接收快取滿時,它會通知對方它的接收允許視窗為0,使傳送方不要再繼續傳送資料。這時接收應用程式從接收快取中讀入一個位元組,那麼就空出了一個位元組的空間。接收TCP馬上告知對方它的接收允許視窗為1。傳送方因此傳送1位元組的資料給接收方。這樣,每當接收應用程式讀取1位元組資料時都會觸發傳送方傳送1位元組資料。顯然每一個數據段僅攜帶如此少的使用者資料是相當低效的。這就是接收方愚笨視窗綜合症。
和接收方一樣,傳送方也會產生這個問題。如果傳送方每次只生成少量的資料,傳送TCP就會傳輸一個攜帶少量使用者資料的資料段。這就是傳送方愚笨視窗綜合症。
要避免這個問題可以採用很簡單的啟發式方法。當接收方有少量的可用快取時,不要急著增加接收允許視窗。同樣,當傳送方有少量的資料要傳送時,也不要急著傳送,等到資料足夠多時再發送。

傳送方愚笨視窗綜合症避免演算法又稱為Nagle演算法。它非常優美簡潔,而且是自適應的。它延遲傳送的時間取決於網路的當前效能,因此能自適應於慢速的和快速的網路。

當然有時候這個演算法是我們所不希望的。在遠端互動式程式中,使用者希望每敲入一個字元都能立即傳送到伺服器上去,而不是等待這個字元被確認後才能傳送下一個字元。可以通過一個TCP選項僅用Nagle演算法。

Nagle演算法:

Nagle演算法是以他的發明人John Nagle的名字命名的,它用於自動連線許多的小緩衝器訊息;這一過程(稱為nagling)通過減少必須傳送包的個數來增加網路軟體系統的效率。Nagle演算法於1984年定義為福特航空和通訊公司IP/TCP擁塞控制方法

,這是福特經營的最早的專用TCP/IP網路減少擁塞控制,從那以後這一方法得到了廣泛應用。Nagle的文件裡定義了處理他所謂的小包問題的方法,這種問題指的是應用程式一次產生一位元組資料,這樣會導致網路由於太多的包而過載(一個常見的情況是傳送端的"愚笨視窗綜合症(Silly Windw Syndrome)")。從鍵盤輸入的一個字元,佔用一個位元組,可能在傳輸上造成41位元組的包,其中包括1位元組的有用資訊和40位元組的標題資料。這種情況轉變成了4000%的消耗,這樣的情況對於輕負載的網路來說還是可以接受的,但是重負載的福特網路就受不了了,它沒有必要在經過節點和閘道器的時候重發,導致包丟失和妨礙傳輸速度。吞吐量可能會妨礙甚至在一定程度上會導致連線失敗。Nagle的演算法通常會在TCP程式裡新增兩行程式碼,在未確認資料傳送的時候讓傳送器把資料送到快取裡。任何資料隨後繼續直到得到明顯的資料確認或者直到攢到了一定數量的資料了再發包。
儘管Nagle的演算法解決的問題只是侷限於福特網路,然而同樣的問題也可能出現在ARPANet。這種方法在包括因特網在內的整個網路裡得到了推廣,成為了預設的執行方式,儘管在高互動環境下有些時候是不必要的,例如在客戶/伺服器情形下。在這種情況下,nagling可以通過使用TCP_NODELAY 插座選項關閉。
Lwip對nagle演算法的解釋如下:儘量將使用者的資料合成一個數據包傳送。只有在以下情況下,會立即傳送:

  • 在本連線上沒有已經發送了,但未被確認的資料。
  •  使用者定義了nodelay標誌位或者infr標誌位(快速重傳)
  • Pcb連線上存在超過1個的未傳送資料
  • Pcb連線的未傳送資料的長度超過mss(就是TCP資料包每次能夠傳輸的最大資料分段)
lwip 中的實現:
/**
 * This is the Nagle algorithm: try to combine user data to send as few TCP
 * segments as possible. Only send if
 * - no previously transmitted data on the connection remains unacknowledged or
 * - the TF_NODELAY flag is set (nagle algorithm turned off for this pcb) or
 * - the only unsent segment is at least pcb->mss bytes long (or there is more
 *   than one unsent segment - with lwIP, this can happen although unsent->len < mss)
 * - or if we are in fast-retransmit (TF_INFR)
 */
#define tcp_do_output_nagle(tpcb) ((((tpcb)->unacked == NULL) || \
                            ((tpcb)->flags & (TF_NODELAY | TF_INFR)) || \
                            (((tpcb)->unsent != NULL) && (((tpcb)->unsent->next != NULL) || \
                              ((tpcb)->unsent->len >= (tpcb)->mss))) \
                            ) ? 1 : 0)