linu 提供如下 IO 接口:
- read 和 write -- 最简单的读写函数
- readn 和 writen -- 原子性读写操作
- recvfrom 和 sendto -- 增加了目标地址和地址结构长度的参数
- recv 和 send -- 允许从进程到内核传递标志
- readv 和 writev -- 允许指定往其中输入数据或从其中输出数据的缓冲区
- recvmsg 和 sendmsg -- 结合了其他IO函数的所有特性,并具备接受和发送辅助数据的能力
他们之间的差异如下:
函数 | 可用任何描述符 | 仅套接字描述符 | 单个读写缓冲区 | 分散读写缓冲区 | 可选标志 | 可选对端地址 | 可选控制信息 |
read write | • | • | |||||
readv writev | • | • | |||||
recv send | • | • | • | ||||
recvfrom sendto | • | • | • | • | |||
recvmsg sendmsg | • | • | • | • | • |
recv 和send
函数原型
ssize_t recv(int sockfd, void *buff, size_t nbytes, int flags);
ssize_t send(int sockfd, const void *buff, size_t nbytes, int flags);
定义于 sys/socket.h 中
调用成功返回输入或输出字节数,出错返回 -1
这两个函数与标准 read 和 write 函数的唯一差别在于增加了一个 flags 参数,当 flags 参数为 0 时,他们与标准的 read 和 write 函数
flag 参数
flags 参数可以取下列表中的一个或多个常值的逻辑或,或取 0 值
参看“recvmsg 和 sendmsg 函数”介绍中的 输入输出函数标志总结表
设定了 MSG_WAITALL 的 recv 函数与 readn 函数完全等价,只有当下列情况之一发生时才会返回比所请求的字节数少的数据:
- 被信号中断
- 连接被终止
- 套接字错误
readv 和 writev
函数原型
ssize_t readv(int filedes, const struct iovec *iov, int iovcnt);
ssize_t writev(int filedes, const struct iovec *iov, int iovcnt);
定义于 sys/uio.h 中
调用成功返回读入或写出的字节数,否则返回-1
函数说明
这两个函数十分类似 read 和 write 函数,但是 readv 和 writev 允许单个系统调用读入或写出自一个或多个缓冲区,分别称作“分散度”和“集中写”,来自读操作的输入数据被分散到多个应用缓冲区中,同时多个应用缓冲区中的输出数据则被集中提供给单个写操作
当然,readv 和 writev 函数可以用于任何描述符,而不仅限于套接字描述符
同时,writev 是一个原子操作,这意味着对于记录的协议(如 UDP),每次 writev 操作只会生成一个数据报
参数说明
- iovec
函数第二个参数是一个保存 iovec 结构数组的首地址的指针,iovec 结构定义在 sys/uio.h 中:
struct iovec
{
void *iov_base;
size_t iov_len;
}
sys/uio.h 中定义了 IOV_MAX 常量,闲置了 iovec 结构数组中元素数目的最大值
iovec 就是缓冲区结构,iov_base 保存了缓冲区首地址,而 iov_len 保存了缓冲区长度
recvmsg 和 sendmsg
函数原型
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
ssize_t sendmsg(int sockfd, struct msghdr *msg, int flags);
定义在 sys/socket.h 中
调用成功返回输入或输出字节数,否则返回 -1
函数说明
这两个函数是最通用的 IO 函数,实际上我们可以把上述所有的 IO 操作都用这两个函数实现
参数说明
- msg
这两个函数把大部分参数封装到了一个 msghdr 结构中:
struct msghdr
{
void *msg_name; // 套接字地址结构
socklen_t msg_namelen;
struct iovec *msg_iov; // 缓存结构数组
int msg_iovlen;
void *msg_control; // 附加数据数组
socklen_t msg_controllen;
int msg_flags;
// 仅供 recvmsg 函数使用,作为值-结果参数返回
}
域 | 说明 |
msg_name | 用于套接字未连接场合(如 UDP),指向一个套接字地址结构的指针,保存接收者(sendmsg)或发送者(recvmsg)的协议地址,如果无需指定协议地址(如 TCP 或已连接 UDP 套接字)则置为空指针 |
msg_namelen | msg_name 所指向地址结构的大小 |
msg_iov | 指向 iovec 结构数组的首地址,该数组即缓冲区数组,在 readv 和 writev 两个函数的介绍中已经介绍了该参数 |
msg_iovlen | 指定 msg_iov 数组中元素个数 |
msg_control | 可选,辅助数据数组的首地址 |
msg_controllen | msg_control 数组中元素个数(对于 recvmsg 函数是一个值-结果参数) |
msg_flags | 标志变量 |
- msg 参数的 msg_flags 和 flags 参数
我们必须区分 msghdr 结构的 msg_flags 成员和 flags 参数:
- 只有 recvmsg 使用 msg_flags 成员,在 recvmsg 被调用时,flags 参数被复制到 msg_flags 成员,并由内核使用其值驱动接收处理过程,并依据结果更新 msg_flags 成员
- sendmsg 则忽略 msg_flags 成员,因为他直接使用 flags 参数驱动发送过程
标志 | 说明 | 发送函数 flags | 接收函数 flags | msg_flags 返回 |
MSG_DONTROUTE | 绕过路由表查找(目的主机在某个直连的本地网络上) | • | ||
MSG_DONTWAIT | 仅本操作非阻塞 | • | • | |
MSG_PEEK | 窥看外来数据 | • | ||
MSG_WAITALL | 等待所有数据(内核不在尚未读入所有请求数目字节之前让它返回) | • | ||
MSG_EOR | 终止记录(这通常对于SOCK_SEQPACKET套接口类型十分有用) | • | • | |
MSG_OOB | 发送或接收带外数据 | • | • | • |
MSG_BCAST | 本数据报作为链路层广播接收或者其目的地址是一个广播地址 | • | ||
MSG_MCAST | 本数据报作为链路层多播收取 | • | ||
MSG_TRUNC | 本数据报被截断(内核预备返回的数据超过进程缓存空间) | • | ||
MSG_CTRUNC | 本数据报的辅助数据被截断(内核预备返回的辅助数据超过 msg_control 所能存储的大小) | • | ||
MSG_NOTIFICATION | 接收到的SCTP带外数据是一个事件通知而不是数据消息 | • |
下图展示了 recvmsg 返回时值的例子:
辅助控制信息(msg_control、msg_controllen 成员)
辅助数据可以通过调用 sendmsg 和 recvmsg 这两个函数使用 msghdr 结构中的 msg_control 和 msg_controllen 成员发送和接收
msg_control 成员是下面介绍的 cmsghdr 结构的数组
struct cmsghdr
{
socklen_t cmsg_len;
int cmsg_level;
int cmsg_type;
unsigned char cmsg_data[];
}
- 辅助数据结构用法:
协议 | cmsg_level | cmsg_type | 说明 |
IPv4 | IPPROTO_IP | IP_REVDSTADDR | 随 UDP 数据报接收目的地址 |
• | • | IP_RECVIF | 随 UDP 数据报接收接口索引 |
IPv6 | IPPROTO_IPV6 | IPV6_DSTOPTS | 指定/接收目的地选项 |
• | • | IPV6_HOPLIMIT | 指定/接收跳限 |
• | • | IPV6_HOPOPTS | 指定/接收步跳选项 |
• | • | IPV6_NEXTHOP | 指定下一跳的地址 |
• | • | IPV6_PKTINFO | 指定/接收分组信息 |
• | • | IPV6_RTHDR | 指定/接收路由首部 |
• | • | IPV6_TCLASS | 指定/接收分组流通类别 |
UNIX域 | SOL_SOCKET | SCM_RIGHTS | 发送/接收描述符 |
• | • | SCM_CREDS | 发送/接收用户凭证 |
- 辅助数据处理函数
下面列出了 5 个宏用来简化对辅助数据的处理
struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *mhdrptr);
// 返回指向第一个 cmsghdr 结构的指针或为 NULL
struct cmsghdr *CMSG_NXTHDR(struct msghdr *mhdrptr, struct cmsghdr *cmsgptr);
// 返回指向 cmhdrptr 下一个辅助对象指针或为 NULL
unsigned char *CMSG_DATA(struct cmsghdr *cmsgptr);
// 返回指向 cmsghdr 结构关联的数据的第一个字节的指针
unsigned int CMSG_LEN(unsigned int length);
// 返回给定数据量下存放到 cmsg_len 中的值
unsigned int CMSG_SPACE(unsigned int length);
// 返回给定数据量下一个辅助数据对象总的大小
文章内容来源于:UNIX IO 操作函数
----------------------------------------------------------------2022.08.28----------------------------------------------------------------
|公|_新的文章内容和附件工程文件
|众|_已更新在博客首页和:
|号|:liwen01
感恩关注,谢谢~