在 socket 编程中,send() 是用于在已连接的套接字上发送数据的系统调用,主要用于 TCP 协议(也可用于 UDP,但需配合连接操作)。它负责将用户态的数据传递到内核缓冲区,再由内核协议栈(如 TCP/IP)完成实际的网络传输。以下从函数原型、参数、返回值、工作原理、使用细节等方面详细讲解:

一、函数原型与头文件

send() 的函数原型如下,需要包含 <sys/socket.h> 头文件:

#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);

二、参数详解

1. sockfd(套接字描述符)
  • 含义:已建立连接的套接字的文件描述符(由 socket() 创建,并通过 connect()accept() 完成连接)。
  • 注意:send() 仅用于已连接的套接字(TCP 中通过 connect() 连接的客户端套接字,或 accept() 返回的服务端连接套接字;UDP 需先通过 connect() 绑定目标地址,否则需用 sendto())。
2. buf(数据缓冲区)
  • 含义:指向用户态内存中待发送数据的缓冲区(如字符串、字节数组等)。
  • 要求:
    • 缓冲区必须是有效的(不能为 NULL),且长度至少为 len 字节。
    • 数据在发送前需由用户确保格式正确(如网络字节序转换、协议封装等)。
3. len(数据长度)
  • 含义:要发送的数据的字节数(size_t 类型,非负整数)。
  • 限制:
    • 实际发送的最大长度受限于内核发送缓冲区大小(可通过 SO_SNDBUF 选项查询/设置)。
    • UDP 中,len 不能超过协议最大传输单元(MTU,通常约 1500 字节),否则可能被分片或丢弃;TCP 无此限制(会自动分片)。
4. flags(标志位)
  • 含义:控制发送行为的标志,可取值为 0 或以下宏的组合(通过位或 | 连接):
    • MSG_OOB:发送带外数据(TCP 紧急数据),用于传递高优先级信息(如中断信号)。带外数据有独立的传输通道,不占用常规数据缓冲区。
    • MSG_DONTWAIT:非阻塞模式发送(覆盖套接字本身的阻塞属性)。若内核缓冲区已满,不会阻塞等待,而是立即返回 -1 并设置 errno = EAGAINEWOULDBLOCK
    • MSG_NOSIGNAL:当连接已关闭(如对端断开)时,不产生 SIGPIPE 信号(默认会产生,可能导致进程终止)。
    • MSG_MORE:告知内核“后续还有数据要发送”,用于批量发送时减少 TCP 分段(优化网络效率)。

三、返回值

  • 成功:返回实际发送的字节数(ssize_t 类型,可能小于 len)。
    原因:内核发送缓冲区可能不足以容纳全部 len 字节数据,此时仅发送部分数据(常见于非阻塞模式或高负载场景)。
  • 失败:返回 -1,并设置 errno 标识错误原因,常见错误码:
    • EBADFsockfd 不是有效的文件描述符。
    • ENOTCONN:套接字未连接(如 TCP 未调用 connect(),或 UDP 未绑定目标地址)。
    • EAGAIN/EWOULDBLOCK:非阻塞模式下,内核缓冲区已满,暂时无法发送(可重试)。
    • EPIPE:连接已断开(如对端关闭),且未设置 MSG_NOSIGNAL(此时可能伴随 SIGPIPE 信号)。
    • EINTR:发送过程被信号中断(可重试)。

四、工作原理

在这里插入图片描述

send() 的核心是“将用户数据复制到内核缓冲区”,而非直接发送到网络,具体流程如下:

  1. 参数校验:内核检查 sockfd 有效性、buf 内存可访问性、len 合理性等。
  2. 缓冲区检查:查看套接字对应的内核发送缓冲区(大小由 SO_SNDBUF 控制)是否有足够空间容纳数据。
  3. 数据复制
    • 若缓冲区有空间,将 buf 中最多 len 字节的数据复制到内核缓冲区(用户态 → 内核态拷贝)。
    • 若空间不足:
      • 阻塞模式:进程进入睡眠,等待缓冲区有空间后再复制(直到超时或被信号唤醒)。
      • 非阻塞模式(MSG_DONTWAIT 或套接字设为 O_NONBLOCK):立即返回 -1 并设置 EAGAIN
  4. 协议处理:内核协议栈(如 TCP)负责后续工作:
    • TCP:将缓冲区数据按 MSS(最大分段大小)分片,添加 TCP 头部,通过 IP 层发送;并维护重传队列,等待对端 ACK 确认后释放缓冲区。
    • UDP(已 connect()):将数据封装成 UDP 数据报,添加头部后直接发送(不保证到达,无确认机制)。
  5. 返回结果:返回实际复制到内核缓冲区的字节数(即“已提交给内核发送的数据量”,而非“已到达对端的数据量”)。

五、关键特性与使用细节

1. 与 write() 的关系

send() 可视为 write() 的“增强版”:

  • write(sockfd, buf, len) 等价于 send(sockfd, buf, len, 0)(即 flags=0 时功能相同)。
  • send() 的优势是通过 flags 支持更多控制(如带外数据、非阻塞等)。
2. 处理“部分发送”

send() 的返回值可能小于 len(如内核缓冲区不足),因此必须循环发送直到所有数据发送完毕,示例:

// 循环发送完整数据
ssize_t send_all(int sockfd, const void *buf, size_t len, int flags) {size_t total_sent = 0;  // 已发送总字节数const char *p = buf;    // 指向未发送数据的指针while (total_sent < len) {ssize_t sent = send(sockfd, p + total_sent, len - total_sent, flags);if (sent == -1) {// 若被信号中断,可重试;其他错误返回失败if (errno == EINTR) continue;return -1;}total_sent += sent;}return total_sent;  // 全部发送成功
}
3. TCP 与 UDP 中的差异
特性TCP 中 send()UDP 中 send()(需先 connect()
连接要求必须通过 connect()/accept() 连接需先 connect() 绑定目标地址(否则用 sendto()
可靠性依赖 TCP 重传机制,保证数据有序到达无可靠性保证,数据可能丢失、乱序
数据边界无边界(粘包),send(100) 可能对应多次 recv有边界(数据报),send(100) 对应一次 recv(100)
发送缓冲区满时行为阻塞等待或返回 EAGAIN(非阻塞)直接丢弃数据并返回 -1errno=EAGAIN
4. 带外数据(MSG_OOB
  • 用途:发送高优先级数据(如“紧急中断”),TCP 会优先传输,对端可通过 SO_OOBINLINE 选项或 recv(..., MSG_OOB) 接收。
  • 限制:带外数据只有 1 字节有效(TCP 紧急指针指向的字节),通常用于“通知”而非传输大量数据。
5. 非阻塞模式注意事项
  • flags=MSG_DONTWAIT 或套接字设为 O_NONBLOCK 时,send() 可能返回 EAGAIN(缓冲区满),此时需通过 select()/poll()/epoll 监听套接字可写事件后再重试。
  • 示例:结合 epoll 处理非阻塞发送:
    // 监听套接字可写事件
    struct epoll_event ev, events[10];
    int epfd = epoll_create(1);
    ev.events = EPOLLOUT;  // 关注可写事件
    ev.data.fd = sockfd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);// 等待可写
    int nfds = epoll_wait(epfd, events, 10, -1);
    for (int i = 0; i < nfds; i++) {if (events[i].data.fd == sockfd && events[i].events & EPOLLOUT) {// 此时缓冲区有空间,可尝试发送ssize_t sent = send(sockfd, buf, len, MSG_DONTWAIT);// 处理发送结果...}
    }
    

六、常见错误与解决

  1. SIGPIPE 信号导致进程终止
    原因:对已关闭的连接调用 send(),默认会产生 SIGPIPE 信号(默认处理是终止进程)。
    解决:发送时设置 MSG_NOSIGNAL 标志,或忽略 SIGPIPE 信号(signal(SIGPIPE, SIG_IGN))。

  2. 发送数据量远小于 len
    原因:内核发送缓冲区过小,或网络拥塞导致缓冲区未及时释放。
    解决:

    • 增大发送缓冲区(setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)))。
    • 采用非阻塞模式 + I/O 多路复用(epoll 等),避免阻塞等待。
  3. UDP 发送失败(errno=EMSGSIZE
    原因:len 超过 MTU 且未开启分片(或对端不支持分片)。
    解决:限制 UDP 数据长度(通常 ≤ 1472 字节,即 MTU 1500 - IP 头部 20 - UDP 头部 8)。

总结

send() 是 socket 编程中发送数据的核心系统调用,其核心功能是将用户数据复制到内核缓冲区,由协议栈完成实际传输。使用时需注意:

  • 处理“部分发送”,通过循环确保数据完整发送;
  • 根据场景选择 flags(如非阻塞、带外数据);
  • 区分 TCP 与 UDP 中的行为差异;
  • 处理常见错误(如 EAGAINSIGPIPE)。

理解 send() 的工作原理(尤其是内核缓冲区的角色)是编写可靠网络程序的关键。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/918279.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/918279.shtml
英文地址,请注明出处:http://en.pswp.cn/news/918279.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Eclipse JSP/Servlet:深入解析与最佳实践

Eclipse JSP/Servlet:深入解析与最佳实践 引言 Eclipse JSP/Servlet 是 Java 企业版(Java EE)的核心技术之一,它为 Web 开发者提供了一个强大的平台来构建动态和交互式的 Web 应用程序。本文将深入探讨 Eclipse JSP/Servlet 的概念、工作原理以及最佳实践,帮助开发者更好…

CNB私有化部署Dify(零成本)教程

本篇文章介绍一下如何进行dify的私有化部署的过程&#xff1a;dify定位是开源的LLM应用平台&#xff0c;搭建自己的这个AI应用&#xff0c;而我们非常熟悉的这个coze实际上是通过搭积木一样搭建AI应用&#xff0c;我选择从dify开始进行了解学习 1.前置准备 我们需要有这个腾讯云…

imx6ull-驱动开发篇16——信号量与互斥体

目录 前言 信号量 概念与特性 信号量 API 函数 互斥体 概念与特性 互斥体 API 函数 前言 Linux 内核提供的几种并发和竞争的处理方法&#xff0c;我们学习了&#xff1a; 驱动开发篇14——原子操作 驱动开发篇15——linux自旋锁 本讲我们就继续学习&#xff1a;信号…

Bug 记录:SecureRandom.getInstanceStrong()导致验证码获取阻塞

问题描述&#xff1a; 在发送验证码到邮件中&#xff0c;接口调用时卡在生成验证码阶段&#xff0c;导致验证码功能完全不可用&#xff1b; 经排查开发环境一切正常&#xff0c;测试环境会重现此问题&#xff1b;问题分析&#xff1a; 现象&#xff1a; 代码卡在 SecureRandom.…

商派小程序商城(小程序/官网/APP···)的范式跃迁与增长再想象

2025年&#xff0c;品牌官方商城应该如何定义&#xff1f;—— 还是一套“电商货架”&#xff1f;在商派看来&#xff0c;现如今“品牌官方商城”则需要重新定义&#xff0c;结合不同品牌企业的业务发展需求&#xff0c;也就有着更多丰富的定义和组合想象——如&#xff0c;商城…

基于串口实现可扩展的硬件函数 RPC 框架(附完整 Verilog 源码)

本文介绍如何使用简单的串口协议搭建一个支持寄存器读写与硬件函数调用的通用交互框架&#xff0c;适用于 FPGA 调试、嵌入式接口、中小型控制系统等场合。 特性&#xff1a; 轻量协议、30 个32位寄存器、函数调用、状态反馈&#xff0c;源码清晰易扩展。&#x1f4a1; 背景与目…

jenkins-飞书通知机制

一. 目标&#xff1a; 触发构建和结束后&#xff0c;自动触发到飞书工作群&#xff0c;发布同学只需要关注消息即可&#xff0c;而无需人工盯梢。 二. 实现方式&#xff1a; 1. 机器人配置&#xff1a; 创建一个群&#xff0c; 配置机器人&#xff1a; 保管好下面的webhoo…

GoLand 项目从 0 到 1:第五天 —— 角色权限中间件实现与事务控制

第五天核心任务:权限校验链路闭环 第五天的开发聚焦于权限控制的核心实现,完成了角色权限中间件的开发,实现了接口级别的权限校验,并基于事务控制确保用户权限操作的数据一致性。通过这部分工作,系统的权限管理从设计阶段正式进入可运行阶段,为后续业务模块的安全接入提…

【工具变量】地级市固定资产投资数据(2000-2023年)

数据简介&#xff1a;地级市固定资产投资是衡量地方经济发展活力与动能的重要指标&#xff0c;其规模、结构及增速不仅反映区域产业布局和政策导向&#xff0c;也直接影响基础设施完善、产业升级和民生改善。清晰展现长期经济发展脉络&#xff0c;助力捕捉经济增长与波动规律。…

Kotlin初体验

前言&#xff1a; 在当今的软件开发领域&#xff0c;随着技术的不断发展&#xff0c;开发者总是在寻找更高效、更简洁的编程语言来提升开发效率并减少代码中的潜在问题。而 Kotlin&#xff0c;作为一门现代的编程语言&#xff0c;逐渐赢得了越来越多开发者的青睐&#xff0c;尤…

五十五、【Linux系统nginx服务】nginx安装、用户认证、https实现

一、Nginx 核心功能全景图 #mermaid-svg-j5M1WUQFrtyrWjAD {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-j5M1WUQFrtyrWjAD .error-icon{fill:#552222;}#mermaid-svg-j5M1WUQFrtyrWjAD .error-text{fill:#552222;s…

AtCoder Beginner Contest 418

文章目录A Im a teapotB Youre a teapotC FlushD XNOR OperationE TrapeziumF Were teapotsG Binary OperationAtCoder Beginner Contest 418A I’m a teapot Takahashi is a teapot. Since he is a teapot, he will gladly accept tea, but will refuse any other liquid. Det…

多级缓存详解

多级缓存 传统缓存&#xff1a; 传统缓存策略一般是请求到达Tomcat后&#xff0c;先查询Redis&#xff0c;如果未命中则查询数据库。 这种模式下请求一定要经过Tomcat处理&#xff0c;Tomcat的性能就成为了整个系统的瓶颈&#xff1b;并且Redis的缓存也有过期时间&#xff0c;一…

接口自动化-JSON Schema

目录 1.介绍 2.安装 3.使用 3.1type关键字 3.2最大值最小值 3.2.1minimum 、 maximum 3.2.2 exclusiveMinimum 、exclusiveMaximum 3.3字符串特殊校验 3.4数据约束 3.5对象约束 3.6必须属性 3.7依赖关系 4.总结 1.介绍 JSON Schema 是一个用来定义和校验 JSON 的…

前端技术架构设计文档(Vue2+Antd+Sass)

前端技术架构设计文档&#xff08;Vue2AntdSass&#xff09; 文档信息项目名称前端系统&#xff08;基于 Vue2 技术栈&#xff09;技术栈核心Vue2 Ant Design Vue Sass版本号V1.0.0技术栈核心Vue2 Ant Design Vue Sass编制日期2025-08-071. 技术栈选型 1.1 核心技术框架类别…

【设计模式】抽象工厂模式 (工具(Kit)模式)

抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;详解一、抽象工厂模式简介 抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09; 是一种 创建型设计模式&#xff08;对象创建型模式&#xff09;&#xff0c;它提供了一种创建一系列相关或相互依赖对象…

Android初学者系统开发学习路线参考

Android初学者系统开发学习路线参考 文章目录Android初学者系统开发学习路线参考一、前言二、Android初学的学习计划第一阶段&#xff08;一个月&#xff09;UI相关学习&#xff1a;开发环境与 UI 基础&#xff0c;第一周&#xff1a;UI 控件与布局进阶&#xff0c;第二周&…

扩散LLM推理新范式:打破生成长度限制,实现动态自适应调节

随着 Gemini-Diffusion&#xff0c;Seed-Diffusion 等扩散大语言模型&#xff08;DLLM&#xff09;的发布&#xff0c;这一领域成为了工业界和学术界的热门方向。但是&#xff0c;当前 DLLM 存在着在推理时必须采用预设固定长度的限制&#xff0c;对于不同任务都需要专门调整才…

【ee类保研面试】其他类---计算机网络

25保研er&#xff0c;希望将自己的面试复习分享出来&#xff0c;供大家参考 part0—英语类 part1—通信类 part2—信号类 part3—高数类 part100—self项目准备 文章目录计算机网络知识点大全**计算机网络知识点总结**一、五层协议模型二、OSI七层模型补充三、TCP 与 UDP 及区别…

Python-机器学习(一)——特征工程

目录 特征工程 一、特征提取 1、字典特征提取 2、文本特征提取 2.1 英文文本提取 2.2 中文文本提取 3、TF-IDF文本特征词的重要程度特征提取 二、无量纲化-预处理 1 MinMaxScaler 归一化 2 normalize归一化 3 StandardScaler 标准化 三、特征降维 1、特征选择 1.…