调用send发送网络数据包一定会立马发送出去吗?
Linux应用层调用了send发送网络数据,那么按照简单的思维,这个动作会触发网卡发送数据,而现实并不是如此!
socket层
首先对于send来说,分为阻塞发送和非阻塞发送:
(1)阻塞操作
内核会检测发送缓冲区是否存在足够的空间存放用户数据,如果空间足够那么直接拷贝数据到socket send buffer,后续发送动作交给协议栈来支持;如果空间不够那么send操作会阻塞,直到内核发送缓冲区空间足够,再把数据拷贝到发送缓冲区,并最后返回用户空间。
(2)非阻塞操作
对于非阻塞操作来说,当发送缓冲区空间不够时send不会阻塞,而是直接返回-1,errno设置为EAGAIN。
以上可知,send仅仅保证了数据存放到了发送缓冲区,而不能保证一定从网卡发出去,因此在socket这一层,发送就已经变为了异步的操作了。
对于TCP协议来说,阻塞操作是在如下的函数中实现的:
int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t size)
{
/*
*
*
*
* 查看socket发送缓冲区是否已经满了
* 如果发送缓冲区满了,对于阻塞操作需要等待
*
*
*
*/
if (!sk_stream_memory_free(sk))
goto wait_for_sndbuf;
......
wait_for_sndbuf:
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
wait_for_memory:
//沒有空闲内存了,需要触发发送缓冲区立即发送,腾出内存空间
//TCP_NAGLE_PUSH表示强制发送,和NODELAY是同样的含义
if (copied)
tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH);
//等待发送缓冲区空间,该函数会导致sleep睡眠.
if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
goto do_error;
......
}
现在我还有另外一个问题,既然send函数并不一定保证数据从网卡出去,那么何时会执行网卡发送动作呢?这个问题在后面一个章节做介绍。
TCP/IP层
如果socket发送缓冲区中空间足够大,send拷贝数据到内核发送缓冲区中,那么实际发送动作是什么时候发生的呢?
实际上send函数并不是仅仅拷贝数据,它会判断缓冲区中的数据量以及当前的协议栈状态,当符合发送条件时就会在send中触发实际的发送动作。
主要的逻辑还是在tcp_sendmsg函数中,我查看了对应的代码,总结了如下几个发送的地方,注意以下所有的发送仅仅是指数据经过协议栈发出,并不代表数据已经被Acked:
情况1:
发现发送的数据量已经超过发送窗口的一半时,设置TCP_NAGLE_PUSH标记,会忽略nagle规则强制发送缓冲区中的所有skb。
情况2:
如果当前skb是未发送skb链表的header,那么它肯定会被发送,设置TCP_NAGLE_PUSH标记,会忽略nagle规则强制发送当前的这一个skb,注意仅仅发送一个。
情况3:
如果发送缓冲区已经满了,需要触发立即发送,腾出内存空间,设置了TCP_NAGLE_PUSH标记,表示忽略nagle规则强制发送缓冲区中的所有skb。
情况4:
当send把一次应用写入的数据都已经写入到发送缓冲区后,退出函数之前,会调用一次发送函数,注意这次发送是需要按照NAGLE规则判断是否触发真实发送的,如果满足Nagle条件才会发送,否则依然会保留在发送缓冲区中。
Nagle
上面介绍了几种可能在send中触发发送的条件,那么其中有一类是需要经过Nagle规则判断的,那么到底什么样的情况是满足Nagle规则的呢?
关于Nagle算法,为了优化小数据包的发送次数,可以累积上层下发的小数据包到一定的程度,再进行组包发送,对于发送端来说,它的发送时机有如下几个:
1.一个包是大于等于MSS长度
2.所有发送包都已经被确认
3.包含有FIN的包
4.包含TCP_NODELAY标记
这里想到一个问题,假如一直发送小包,按照Nagle算法,实际发送函数结束并不会把小包发送出去,而是等待小包合并为大包后才发送,那么假如一直没有小包过来合并,那么按照上述规则,这个小包是不是永远都不发送了呢?实际上并不是的,而是在TCP接收处理时会做处理,可以看上面的第二个规则:
当所有发送包都已经被确认时,即使当前发送缓冲区中存在小包,那么也不继续等待了,而是直接发送出去。另外如果一直没有接收到ACK,当时间超时也会触发发送动作。
linux source code:linux-3.10.108
上一篇: react中收集表单数据