欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

调用send发送网络数据包一定会立马发送出去吗?

程序员文章站 2024-02-26 23:20:04
...

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

相关标签: 网络子系统