background image

JAVA 编程中 Nagle 算法和 Delayed ACK 的测试 

Nagle 算法的立意是良好的,避免网络中充塞小封包,提高网络的利用率。但是当 Nagle 算
法遇到

delayed ACK 悲剧就发生了。Delayed ACK 的本意也是为了提高 TCP 性能,跟应答数

据捎带上

ACK,同时避免糊涂窗口综合症,也可以一个 ack 确认多个段来节省开销。

悲剧发生在这种情况,假设一端发送数据并等待另一端应答,协议上分为头部和数据,发
送的时候不幸地选择了

write-write,然后再 read,也就是先发送头部,再发送数据,最后

等待应答。发送端的伪代码是这样

write(head);
write(body);
read(response);
接收端的处理代码类似这样:

read(request);
process(request);
write(response);
这里假设

head 和 body 都比较小,当默认启用 nagle 算法,并且是第一次发送的时候,根据

nagle 算法,第一个段 head 可以立即发送,因为没有等待确认的段;接收端收到 head,但
是包不完整,继续等待

body 达到并延迟 ACK;发送端继续写入 body,这时候 nagle 算法起

作用了,因为

head 还没有被 ACK,所以 body 要延迟发送。这就造成了发送端和接收端都在

等待对方发送数据的现象,发送端等待接收端

ACK head 以便继续发送 body,而接收端在

等待发送方发送

body 并延迟 ACK,悲剧的无以言语。这种时候只有等待一端超时并发送数

据才能继续往下走。

正因为

nagle 算法和 delayed ack 的影响,再加上这种 write-write-read 的编程方式造成了很

多网贴在讨论为什么自己写的网络程序性能那么差。然后很多人会在帖子里建议禁用

Nagle

算法吧,设置

TCP_NODELAY 为 true 即可禁用 nagle 算法。但是这真的是解决问题的唯一办

法和最好办法吗?

其实问题不是出在

nagle 算法身上的,问题是出在 write-write-read 这种应用编程上。禁用

nagle 算法可以暂时解决问题,但是禁用 nagle 算法也带来很大坏处,网络中充塞着小封包,
网络的利用率上不去,在极端情况下,大量小封包导致网络拥塞甚至崩溃。因此,能不禁止
还是不禁止的好,后面我们会说下什么情况下才需要禁用

nagle 算法。对大多数应用来说,

一般都是连续的请求

——应答模型,有请求同时有应答,那么请求包的 ACK 其实可以延迟

到跟响应一起发送,在这种情况下,其实你只要避免

write-write-read 形式的调用就可以避

免延迟现象,利用

writev 做聚集写或者将 head 和 body 一起写,然后再 read,变成 write-

read-write-read 的形式来调用,就无需禁用 nagle 算法也可以做到不延迟。