linux网络编程学习笔记(5)——数据收发和IO操作

linu 提供如下 IO 接口:

  1. read 和 write -- 最简单的读写函数
  2. readn 和 writen -- 原子性读写操作
  3. recvfrom 和 sendto -- 增加了目标地址和地址结构长度的参数
  4. recv 和 send -- 允许从进程到内核传递标志
  5. readv 和 writev -- 允许指定往其中输入数据或从其中输出数据的缓冲区
  6. recvmsg 和 sendmsg -- 结合了其他IO函数的所有特性,并具备接受和发送辅助数据的能力

他们之间的差异如下:

5 组 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 函数完全等价,只有当下列情况之一发生时才会返回比所请求的字节数少的数据:

  1. 被信号中断
  2. 连接被终止
  3. 套接字错误

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 操作只会生成一个数据报

参数说明

  1. iovec

函数第二个参数是一个保存 iovec 结构数组的首地址的指针,iovec 结构定义在 sys/uio.h 中:

1 struct iovec
2 {
3     void *iov_base;
4     size_t iov_len;
5 }

 sys/uio.h 中定义了 IOV_MAX 常量,闲置了 iovec 结构数组中元素数目的最大值

iovec 就是缓冲区结构,iov_base 保存了缓冲区首地址,而 iov_len 保存了缓冲区长度


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 函数使用,作为值-结果参数返回
}
 
msghdr 结构说明
说明
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 参数:

  1. 只有 recvmsg 使用 msg_flags 成员,在 recvmsg 被调用时,flags 参数被复制到 msg_flags 成员,并由内核使用其值驱动接收处理过程,并依据结果更新 msg_flags 成员
  2. 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 结构的数组

1 struct cmsghdr
2 {
3 socklen_t cmsg_len;
4 int cmsg_level;
5 int cmsg_type;
6 unsigned char cmsg_data[];
7 }
  • 辅助数据结构用法:
辅助数据用法
协议 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 个宏用来简化对辅助数据的处理

 1 struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *mhdrptr);
 2 // 返回指向第一个 cmsghdr 结构的指针或为 NULL
 3 struct cmsghdr *CMSG_NXTHDR(struct msghdr *mhdrptr, struct cmsghdr *cmsgptr);
 4 // 返回指向 cmhdrptr 下一个辅助对象指针或为 NULL
 5 unsigned char *CMSG_DATA(struct cmsghdr *cmsgptr);
 6 // 返回指向 cmsghdr 结构关联的数据的第一个字节的指针
 7 unsigned int CMSG_LEN(unsigned int length);
 8 // 返回给定数据量下存放到 cmsg_len 中的值
 9 unsigned int CMSG_SPACE(unsigned int length);
10 // 返回给定数据量下一个辅助数据对象总的大小

 文章内容来源于:https://techlog.cn/article/list/10182663

猜你喜欢

转载自www.cnblogs.com/WenLee/p/12159041.html