1. 程式人生 > >TCP之Nagle算法&&延遲ACK

TCP之Nagle算法&&延遲ACK

nagle 函數註釋 關閉 而在 tput under cor 不能 定時

1. Nagle算法:

是為了減少廣域網的小分組數目,從而減小網絡擁塞的出現;

該算法要求一個tcp連接上最多只能有一個未被確認的未完成的小分組,在該分組ack到達之前不能發送其他的小分組,tcp需要收集這些少量的分組,並在ack到來時以一個分組的方式發送出去;其中小分組的定義是小於MSS的任何分組;(已經有一個小分組待確認,則下一個小分組禁止發送)。

該算法的優越之處在於它是自適應的,確認到達的越快,數據也就發哦送的越快;而在希望減少微小分組數目的低速廣域網上,則會發送更少的分組;

Nagle算法的規則(可參考tcp_output.c文件裏tcp_nagle_check函數註釋):

(1)如果包長度達到MSS,則允許發送; (2)如果該包含有FIN,則允許發送; (3)設置了TCP_NODELAY選項,則允許發送; (4)未設置TCP_CORK
選項時,若所有發出去的小數據包(包長度小於MSS)均被確認,則允許發送; (5)上述條件都未滿足,但發生了超時(一般為200ms),則立即發送。

所謂的CORK就是塞子的意思,形象地理解就是用CORK將連接塞住,使得數據先不發出去,等到拔去塞子後再發出去。設置該選項後,內核會盡力把小數據包拼接成一個大的數據包(一個MTU)再發送出去,當然若一定時間後(一般為200ms,該值尚待確認),內核仍然沒有組合成一個MTU時也必須發送現有的數據(不可能讓數據一直等待吧)。
  然而,TCP_CORK的實現可能並不像你想象的那麽完美,CORK並不會將連接完全塞住。內核其實並不知道應用層到底什麽時候會發送第二批數據用於和第一批數據拼接以達到MTU的大小,因此內核會給出一個時間限制,在該時間內沒有拼接成一個大包(努力接近MTU)的話,內核就會無條件發送。也就是說若應用層程序發送小包數據的間隔不夠短時,TCP_CORK就沒有一點作用,反而失去了數據的實時性(每個小包數據都會延時一定時間再發送)。

2. 延遲ACK:

如果tcp對每個數據包都發送一個ack確認,那麽只是一個單獨的數據包為了發送一個ack代價比較高,所以tcp會延遲一段時間,如果這段時間內有數據發送到對端,則捎帶發送ack,如果在延遲ack定時器觸發時候,發現ack尚未發送,則立即單獨發送;

延遲ACK好處:

(1) 避免糊塗窗口綜合癥;

(2) 發送數據的時候將ack捎帶發送,不必單獨發送ack;

(3) 如果延遲時間內有多個數據段到達,那麽允許協議棧發送一個ack確認多個報文段;

3. 當Nagle遇上延遲ACK:

試想如下典型操作,寫-寫-讀,即通過多個寫小片數據向對端發送單個邏輯的操作,兩次寫數據長度小於MSS,當第一次寫數據到達對端後,對端延遲ack,不發送ack,而本端因為要發送的數據長度小於MSS,所以nagle算法起作用,數據並不會立即發送,而是等待對端發送的第一次數據確認ack;這樣的情況下,需要等待對端超時發送ack,然後本段才能發送第二次寫的數據,從而造成延遲;

4. 關閉Nagle算法:

使用TCP套接字選項TCP_NODELAY可以關閉套接字選項;

如下場景考慮關閉Nagle算法:

(1) 對端不向本端發送數據,並且對延時比較敏感的操作;這種操作沒法捎帶ack;

(2) 如上寫-寫-讀操作;對於此種情況,優先使用其他方式,而不是關閉Nagle算法:

--使用writev,而不是兩次調用write,單個writev調用會使tcp輸出一次而不是兩次,只產生一個tcp分節,這是首選方法;

--把兩次寫操作的數據復制到單個緩沖區,然後對緩沖區調用一次write;

--關閉Nagle算法,調用write兩次;有損於網絡,通常不考慮;

5. 禁止Nagle和開啟Nagle算法發送數據與確認示意圖:

技術分享圖片

TCP之Nagle算法&&延遲ACK