Nagle算法的目的是避免TCP发送大量的小数据包。TCP在接收到前一个小段的ACK消息之前,一直保存
小数据包。
Nagle算法通常在实现时要求做到,如果存在任何未确认的数据就不能发送小数据包。
BSD的实现是允许在空闲链接上发送大的写操作剩下的最后的小段。
Nagle算法是silly window syndrome(SWS)预防算法的一个半集。SWS算法预防发送少量的数据,
Nagle算法是其在发送方的实现,而接收方要做的时不要通告缓冲空间的很小增长,不通知小窗口,除非
缓冲区空间有显著的增长。这里显著的增长定义为完全大小的段(MSS)或增长到大于最大窗口的一半。
linux中的Nagle检测源码
/* 该函数返回1表示还有小包没有确认
* snd_sml保存的是最近发送的小包的序号
*/
static inline int tcp_minshall_check(const struct tcp_sock *tp)
{
return after(tp->snd_sml, tp->snd_una) &&
!after(tp->snd_sml, tp->snd_nxt);
}
/* 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.
* 反之,Nagle算法使用的情况是满足以下条件
* 1 数据包小于MSS
* 2 TCP_CORK设置了,或,没设置TCP_NODELAY并且上一个小包没有确认
*/
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))));
}
/* Return non-zero if the Nagle test allows this packet to be
* sent now.
*/
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;
}
对于紧急数据以及FIN包,F-RTO包,要忽略NAGLE。
nagle算法的一个问题是会与TCP的延迟确认机制发生冲突。举例如下:
如果客户端发送一个请求,但是却以2个数据包(第一个大概是包头信息)的方式发送。
当第一个数据包发送后,Nagle机制启用,不能发送第2个数据包,除非得到对方的ACK确认;
但是由于TCP的机制是延迟确认,也就是如果当前没有数据发送捎带ACK的话,就在200ms(linux)
发送ACK。这样造成的延迟是很客观的。
解决的办法是禁止nagle算法,不过这显然是有失本意的(大量小数据包的发生);
所以在发送数据的时候要尽量使用聚集写(writev)。