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

2023-10-07 07:32

本文主要是介绍调用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

这篇关于调用send发送网络数据包一定会立马发送出去吗?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/156971

相关文章

Python调用另一个py文件并传递参数常见的方法及其应用场景

《Python调用另一个py文件并传递参数常见的方法及其应用场景》:本文主要介绍在Python中调用另一个py文件并传递参数的几种常见方法,包括使用import语句、exec函数、subproce... 目录前言1. 使用import语句1.1 基本用法1.2 导入特定函数1.3 处理文件路径2. 使用ex

Java中Springboot集成Kafka实现消息发送和接收功能

《Java中Springboot集成Kafka实现消息发送和接收功能》Kafka是一个高吞吐量的分布式发布-订阅消息系统,主要用于处理大规模数据流,它由生产者、消费者、主题、分区和代理等组件构成,Ka... 目录一、Kafka 简介二、Kafka 功能三、POM依赖四、配置文件五、生产者六、消费者一、Kaf

Python手搓邮件发送客户端

《Python手搓邮件发送客户端》这篇文章主要为大家详细介绍了如何使用Python手搓邮件发送客户端,支持发送邮件,附件,定时发送以及个性化邮件正文,感兴趣的可以了解下... 目录1. 简介2.主要功能2.1.邮件发送功能2.2.个性签名功能2.3.定时发送功能2. 4.附件管理2.5.配置加载功能2.6.

Idea调用WebService的关键步骤和注意事项

《Idea调用WebService的关键步骤和注意事项》:本文主要介绍如何在Idea中调用WebService,包括理解WebService的基本概念、获取WSDL文件、阅读和理解WSDL文件、选... 目录前言一、理解WebService的基本概念二、获取WSDL文件三、阅读和理解WSDL文件四、选择对接

解决Cron定时任务中Pytest脚本无法发送邮件的问题

《解决Cron定时任务中Pytest脚本无法发送邮件的问题》文章探讨解决在Cron定时任务中运行Pytest脚本时邮件发送失败的问题,先优化环境变量,再检查Pytest邮件配置,接着配置文件确保SMT... 目录引言1. 环境变量优化:确保Cron任务可以正确执行解决方案:1.1. 创建一个脚本1.2. 修

SSID究竟是什么? WiFi网络名称及工作方式解析

《SSID究竟是什么?WiFi网络名称及工作方式解析》SID可以看作是无线网络的名称,类似于有线网络中的网络名称或者路由器的名称,在无线网络中,设备通过SSID来识别和连接到特定的无线网络... 当提到 Wi-Fi 网络时,就避不开「SSID」这个术语。简单来说,SSID 就是 Wi-Fi 网络的名称。比如

Java实现任务管理器性能网络监控数据的方法详解

《Java实现任务管理器性能网络监控数据的方法详解》在现代操作系统中,任务管理器是一个非常重要的工具,用于监控和管理计算机的运行状态,包括CPU使用率、内存占用等,对于开发者和系统管理员来说,了解这些... 目录引言一、背景知识二、准备工作1. Maven依赖2. Gradle依赖三、代码实现四、代码详解五

Java调用Python代码的几种方法小结

《Java调用Python代码的几种方法小结》Python语言有丰富的系统管理、数据处理、统计类软件包,因此从java应用中调用Python代码的需求很常见、实用,本文介绍几种方法从java调用Pyt... 目录引言Java core使用ProcessBuilder使用Java脚本引擎总结引言python

Django中使用SMTP实现邮件发送功能

《Django中使用SMTP实现邮件发送功能》在Django中使用SMTP发送邮件是一个常见的需求,通常用于发送用户注册确认邮件、密码重置邮件等,下面我们来看看如何在Django中配置S... 目录1. 配置 Django 项目以使用 SMTP2. 创建 Django 应用3. 添加应用到项目设置4. 创建

java如何调用kettle设置变量和参数

《java如何调用kettle设置变量和参数》文章简要介绍了如何在Java中调用Kettle,并重点讨论了变量和参数的区别,以及在Java代码中如何正确设置和使用这些变量,避免覆盖Kettle中已设置... 目录Java调用kettle设置变量和参数java代码中变量会覆盖kettle里面设置的变量总结ja