Nagle算法簡介
Nagle算法主要是避免發送小的數據包,要求TCP連接上最多只能有一個未被確認的小分組,在該分組的確認到達之前不能發送其他的小分組。
在默認的情況下,Nagle算法是默認開啟的,Nagle算法比較適用于發送方發送大批量的小數據,并且接收方作出及時回應的場合,這樣可以降低包的傳輸個數。
但是如果你的程序是 write-write-read 模式,在使用了Nagle算法后,第二個 write 就會被推后一個RRT發送而造成一個很長的ack等待,從而產生一個延遲。為了避免這種情況,一般建議在應用層做緩沖,將兩個write合在一起,成為 write-read。
代碼分析
我們通過一個例子觀察下Nagle算法的延遲
- 服務器端代碼:recipes/tpc/nodelay_server.cc
- 客戶端代碼:recipes/tpc/nodelay.cc
客戶端代碼
// ...... 僅展示出部分代碼if (tcpnodelay){// 設置tcp TCP_NODELAYstream->setTcpNoDelay(true);printf("connected, set TCP_NODELAY\n");}else{stream->setTcpNoDelay(false);printf("connected\n");}for (int n = 0; n < num; ++n){printf("Request no. %d, sending %d bytes\n", n, len);if (buffering){std::vector<char> message(len + sizeof len, 'S');memcpy(message.data(), &len, sizeof len);int nw = stream->sendAll(message.data(), message.size());printf("%.6f sent %d bytes\n", now(), nw);}else{// 先發送頭 在發送數據 驗證Nagel算法stream->sendAll(&len, sizeof len);printf("%.6f sent header\n", now());usleep(1000); // prevent kernel merging TCP segmentsstd::string payload(len, 'S');int nw = stream->sendAll(payload.data(), payload.size());printf("%.6f sent %d bytes\n", now(), nw);}}
上面可以看出,正常下我們開啟Nagel,并發送header和data兩個數據包,我們還可以合并header和data,將其合成一個包發送,此外,我們還可以設置TCP_NODELAY選項。
測試
環境:兩臺橋聯的虛擬機
我們使用ping命令測試一下兩臺機器正常情況下的延遲
wang@localhost tpc]$ ping 192.168.1.104
PING 192.168.1.104 (192.168.1.104) 56(84) bytes of data.
64 bytes from 192.168.1.104: icmp_seq=1 ttl=64 time=4.30 ms
64 bytes from 192.168.1.104: icmp_seq=2 ttl=64 time=4.08 ms
64 bytes from 192.168.1.104: icmp_seq=3 ttl=64 time=4.26 ms
64 bytes from 192.168.1.104: icmp_seq=4 ttl=64 time=3.06 ms
64 bytes from 192.168.1.104: icmp_seq=5 ttl=64 time=3.77 ms
^C
--- 192.168.1.104 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4016ms
rtt min/avg/max/mdev = 3.064/3.900/4.307/0.459 ms
大概在4ms左右
開啟Nagle發送兩個數據包
[wang@localhost tpc]$ ./nodelay 192.168.1.104 3210
connecting to 192.168.1.104:3210
connected
Request no. 0, sending 3210 bytes
1715777496.470708 sent header
1715777496.478341 sent 3210 bytes
Sent all 1 requests, receiving responses.
1715777496.483114 received 4 bytes, ack = 3210
total 0.013132 seconds
大概在13ms左右
關閉Nagle發送兩個數據包
[wang@localhost tpc]$ ./nodelay -D 192.168.1.104 3210
connecting to 192.168.1.104:3210
connected, set TCP_NODELAY
Request no. 0, sending 3210 bytes
1715777794.851212 sent header
1715777794.855121 sent 3210 bytes
Sent all 1 requests, receiving responses.
1715777794.856854 received 4 bytes, ack = 3210
total 0.006093 seconds
可以看見,關閉Nagel后,延遲大約6ms左右
開啟Nagel合并發送一個數據包
[wang@localhost tpc]$ ./nodelay -b 192.168.1.104 3210
connecting to 192.168.1.104:3210
connected
Request no. 0, sending 3210 bytes
1715778177.438170 sent 3214 bytes
Sent all 1 requests, receiving responses.
1715778177.441433 received 4 bytes, ack = 3210
total 0.004178 seconds
因為只發送一個,延遲跟ping差不多,大概4ms左右