一、send()
函数概述
1.1 定义与作用
send()
函数用于将数据发送到已建立连接的套接字上,主要用于 面向连接的套接字(如 TCP)。它提供了比 write()
更丰富的功能,可以通过参数指定发送行为。
1.2 函数原型
|
|
二、send()
函数的参数与返回值
2.1 参数解析
sockfd
:套接字文件描述符,由socket()
函数创建并返回,表示通信的端点。buf
:指向要发送的数据缓冲区的指针。len
:要发送的数据长度(字节数)。flags
:发送选项,控制发送行为。常用的标志包括:0
:默认,无特殊选项。MSG_DONTWAIT
:非阻塞发送。MSG_NOSIGNAL
:避免发送SIGPIPE
信号。
2.2 返回值
- 成功:返回实际发送的字节数。
- 失败:返回
-1
,并设置errno
,指示具体的错误原因。
三、send()
函数的工作原理
3.1 数据发送过程
- 用户空间到内核空间:
send()
将用户空间的数据复制到内核空间的发送缓冲区。 - 发送缓冲区管理:如果发送缓冲区已满,
send()
会阻塞(阻塞模式)或返回错误(非阻塞模式)。 - 协议处理:内核协议栈处理数据,根据协议(如 TCP)进行封装和传输。
- 数据传输:通过网络将数据发送到目标主机。
3.2 阻塞与非阻塞行为
- 阻塞模式:当发送缓冲区已满时,
send()
会阻塞,直到有足够空间。 - 非阻塞模式:
send()
立即返回,若无法发送,则返回-1
,并设置errno
为EAGAIN
或EWOULDBLOCK
。
3.3 与 write()
的区别
- 功能:
send()
提供了flags
参数,可控制发送行为;write()
没有此功能。 - 适用范围:
send()
专用于套接字通信;write()
可用于文件、管道等。
四、send()
的使用示例
4.1 发送字符串数据
|
|
4.2 处理发送缓冲区满的情况
|
|
五、使用注意事项
5.1 部分发送与循环发送
- 现象:
send()
可能无法一次发送完所有数据,需循环发送。 - 原因:网络拥塞、发送缓冲区限制等。
- 解决方案:在发送数据时,使用循环,不断调用
send()
,直到所有数据发送完毕。
5.2 信号中断
- 问题:
send()
可能被信号中断(EINTR
),需要处理此情况。 - 解决方案:在收到
EINTR
时,重新调用send()
。
5.3 SIGPIPE 信号
-
现象:向已关闭的套接字发送数据,会触发
SIGPIPE
信号,默认行为是终止进程。 -
解决方案:
-
捕获并忽略
SIGPIPE
信号:1 2
#include <signal.h> signal(SIGPIPE, SIG_IGN);
-
使用
MSG_NOSIGNAL
标志:1
send(sockfd, buf, len, MSG_NOSIGNAL);
-
5.4 非阻塞模式处理
- 问题:在非阻塞套接字上,
send()
可能返回-1
,并设置errno
为EAGAIN
或EWOULDBLOCK
。 - 解决方案:在程序中处理这种情况,采用异步或事件驱动方式。
六、常见问题
6.1 忘记检查返回值
- 问题:未检查
send()
的返回值,无法确定数据是否成功发送。 - 建议:始终检查返回值,处理可能的错误和部分发送情况。
6.2 缓冲区管理不当
- 问题:未考虑发送缓冲区的容量,导致程序阻塞或数据丢失。
- 解决方案:在高并发或大量数据传输时,合理设置发送缓冲区大小,或使用非阻塞模式结合事件通知。
6.3 数据完整性
- 问题:未处理部分发送,导致数据丢失或不完整。
- 解决方案:实现循环发送,确保所有数据都被发送。
6.4 数据序列化与协议设计
- 问题:直接发送复杂数据结构,可能导致数据不一致或兼容性问题。
- 解决方案:对数据进行序列化,设计良好的通信协议,确保数据的可解析性和兼容性。
七、send()
函数的高级用法
7.1 使用 flags
控制发送行为
MSG_DONTWAIT
:非阻塞发送,即使套接字未设置为非阻塞模式。MSG_OOB
:发送带外数据(紧急数据),用于 TCP 套接字的紧急模式。MSG_EOR
:标记数据的结束,用于记录分界。
7.2 发送文件描述符(UNIX 域套接字)
- 场景:在 UNIX 域套接字中,可以通过
sendmsg()
发送文件描述符,实现进程间共享文件描述符。 - 实现:构造
msghdr
结构体,使用控制消息(cmsghdr
)传递文件描述符。
7.3 高级 I/O 模型的结合
- 异步 I/O:结合
epoll
、kqueue
等机制,实现高性能的网络通信。 - 零拷贝发送:使用
sendfile()
函数,可以将文件数据直接从内核空间发送,减少数据拷贝,提高效率。
八、拓展资料:关键概念解释
8.1 套接字缓冲区
- 发送缓冲区:内核为每个套接字维护的缓冲区,用于存储待发送的数据。
- 接收缓冲区:用于存储接收到但未被应用程序读取的数据。
- 设置缓冲区大小:可以使用
setsockopt()
函数设置SO_SNDBUF
和SO_RCVBUF
,调整缓冲区大小。
8.2 阻塞与非阻塞模式
-
阻塞模式:默认模式,I/O 操作会阻塞,直到完成或发生错误。
-
非阻塞模式:I/O 操作立即返回,若无法完成,则返回错误。
-
设置非阻塞模式:
1 2 3
#include <fcntl.h> int flags = fcntl(sockfd, F_GETFL, 0); fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
8.3 I/O 多路复用
- 概念:使用单个线程或进程监控多个文件描述符的状态,提高资源利用率。
- 常用机制:
select()
、poll()
、epoll()
、kqueue()
等。 - 应用场景:高并发网络服务器,实时性要求高的应用。
8.4 信号处理
SIGPIPE
信号:向已关闭的套接字发送数据,会触发此信号。- 处理方式:
- 忽略信号:
signal(SIGPIPE, SIG_IGN);
- 屏蔽信号:使用
sigaction()
函数。 - 使用
MSG_NOSIGNAL
标志,避免触发信号。
- 忽略信号:
九、示例代码:完整的客户端发送程序
|
|