一、背景
先谈一下TCP nagle算法在实际网络中的表现。
当有一个TCP数据段不足MSS,比如要发送700Byte数据,MSS为1460Byte的情况。nagle算法会延迟这个数据段的发送,等待,直到有足够的数据填充成一个完整数据段。也许有人会问,这有什么影响呢?没有太大的影响,总体上来说,这种措施能节省不必要的资源消耗。但是要发送的总体数据很小时,这种措施就是拖后腿了。比如,用户请求一个网页,大约十几KB的数据,TCP先发送了八九个数据包,剩下几百字节一直不发送,要等到另一个RTT才发送,这时候前面发送数据的ACK已经返回了。这样的用户体验是很不好的。 所以,现在很多服务器都选择主动关闭nagle算法,因为带宽够大,资源消耗不是问题,速度反而是个大问题。
当有一个TCP数据段不足MSS,比如要发送700Byte数据,MSS为1460Byte的情况。nagle算法会延迟这个数据段的发送,等待,直到有足够的数据填充成一个完整数据段。也许有人会问,这有什么影响呢?没有太大的影响,总体上来说,这种措施能节省不必要的资源消耗。但是要发送的总体数据很小时,这种措施就是拖后腿了。比如,用户请求一个网页,大约十几KB的数据,TCP先发送了八九个数据包,剩下几百字节一直不发送,要等到另一个RTT才发送,这时候前面发送数据的ACK已经返回了。这样的用户体验是很不好的。 所以,现在很多服务器都选择主动关闭nagle算法,因为带宽够大,资源消耗不是问题,速度反而是个大问题。
二、Nagle算法的起源和机制
nagle算法由John Nagle发明,最初是为了解决福特航空通信的拥塞控制。
这要从糊涂窗口综合症说起,糊涂窗口综合症指的是报文段有效负载过小。产生的原因有两类:1.发送方产生数据过慢;2.接收方吸收数据过慢。详细解释一下:1.如果发送方是远程登录,每次传输的数据可能只有几个字节。占报文段比重很小。2.接收方没有足够的缓存和处理速度,总是通告过小的窗口,导致数据包有效负荷减少。相应的解决方案也有两种:1.发送端使用Nagle算法;2.接收端使用clark算法或者delay ack。发送方Nagle算法的实质是拼接小数据包,如果当前数据不够一个MSS,则等待,直到有足够的数据能拼到一起或者新的ACK到来。接收方Clark算法是一有数据包就发送ACK,但通告窗口为0,直到有足够的接收缓存,再通告较大的窗口,delay ack是指不立即回复ACK,直到有多个数据包到来,或者有足够的缓冲区。
linux-2.6.32.12/net/ipv4/tcp_output.c
tcp_write_xmit函数,发送数据主循环
这一段代码的意思是,如果要发送的数据不够大,不能够分段时,调用tcp_nagle_test进行发送检查。
如果nonagle已经启用,则返回1,表示不使用nagle算法,立即发送数据。对于有紧急标志位的报文段或者最后的FIN,以及虚假RTO的情况,也不启用nagle算法,立即发送数据。
最后的tcp_nagle_check:
该函数返回0表示不启用nagle算法。如果当前报文段数据小于MSS,或者包含FIN,或者已经设置TCP_NODELAY系统参数,或者没有设置TCP_CORK选项,而所有已发出的包都已经被确认了。TCP_CORK是从另一种角度对NAGLE算法的优化。TCP_NODELAY本质在于立即发送,而TCP_CORK比NAGLE算法等待发送的时间更长,只有当数据包大于一个MSS或者取消了TCP_CORK选项时,才能发送[5]。
NAGLE算法和DELAY ACK机制的分析至此告一段落。下面将针对各个不同版本的TCP协议进行分析讲解。
引用:
[1] http://www.cnblogs.com/zhaoyl/archive/2012/09/20/2695799.html 糊涂窗口综合症和nagle算法
[2] http://283631583.blog.163.com/blog/static/787455022008102505836334/ 糊涂窗口综合症和nagle算法
[3] Nagle算法RFC标准896
[4] http://www.slyar.com/blog/c-operator-priority.html C算符优先级
[5] TCP_CORK
tcp_write_xmit函数,发送数据主循环
-
if (tso_segs == 1) {
-
if (unlikely(!tcp_nagle_test(tp, skb, mss_now,
-
(tcp_skb_is_last(sk, skb) ?
-
nonagle : TCP_NAGLE_PUSH))))
-
break;
-
} else {
-
if (!push_one && tcp_tso_should_defer(sk, skb))
-
break;
- }
-
static inline int tcp_nagle_test(struct tcp_sock *tp, struct sk_buff *skb,
-
unsigned int cur_mss, int nonagle)
-
{
-
/* Nagle rule does not apply to frames, which sit in the middle of the
-
* write_queue (they have no chances to get new data).
-
*
-
* This is implemented in the callers, where they modify the 'nonagle'
-
* argument based upon the location of SKB in the send queue.
-
*/
-
if (nonagle & TCP_NAGLE_PUSH)
-
return 1;
-
-
/* Don't use the nagle rule for urgent data (or for the final FIN).
-
* Nagle can be ignored during F-RTO too (see RFC4138).
-
*/
-
if (tcp_urg_mode(tp) || (tp->frto_counter == 2) ||
-
(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN))
-
return 1;
-
-
if (!tcp_nagle_check(tp, skb, cur_mss, nonagle))
-
return 1;
-
-
return 0;
- }
最后的tcp_nagle_check:
-
/* Return 0, if packet can be sent now without violation Nagle's rules:
-
* 1. It is full sized.
-
* 2. Or it contains FIN. (already checked by caller)
-
* 3. Or TCP_NODELAY was set.
-
* 4. Or TCP_CORK is not set, and all sent packets are ACKed.
-
* With Minshall's modification: all sent small packets are ACKed.
-
*/
-
static inline int tcp_nagle_check(const struct tcp_sock *tp,
-
const struct sk_buff *skb,
-
unsigned mss_now, int nonagle)
-
{
-
return (skb->len < mss_now &&
-
((nonagle & TCP_NAGLE_CORK) ||
-
(!nonagle && tp->packets_out && tcp_minshall_check(tp))));
- }
-
/* Flags in tp->nonagle */
-
#define TCP_NAGLE_OFF 1 /* Nagle's algo is disabled */
-
#define TCP_NAGLE_CORK 2 /* Socket is corked */
- #define TCP_NAGLE_PUSH 4 /* Cork is overridden for already queued data */
引用:
[1] http://www.cnblogs.com/zhaoyl/archive/2012/09/20/2695799.html 糊涂窗口综合症和nagle算法
[2] http://283631583.blog.163.com/blog/static/787455022008102505836334/ 糊涂窗口综合症和nagle算法
[3] Nagle算法RFC标准896
[4] http://www.slyar.com/blog/c-operator-priority.html C算符优先级
[5] TCP_CORK