这个两个函数是通用的I/O函数。实际上可以把所有read,readv,recv,recvfrom调用替换成recvmsg调用;类似的,各种输出函数也可以替换成sedmsg调用。
函数原型:
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
ssize_t sendmsg(int sockfd, struct msghdr *msg, int flags);
这两个函数把大部分参数封装到一个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;
};
msg_name和msg_namelen用于套接字未连接的场合(譬如未连接的udp套接字)。它们类似于recvfrom和sendto的第五个和第六个参数。
msg_name:指向一个套接字地址结构,用户存放接收者(对于sendmsg)或发送者(对于sendmsg)的协议地址。如果无需指明协议地址(例如tcp套接字或已连接udp套接字),msg_name应置为空。
msg_namelen:对于sendmsg是一个参数,对recvmsg却是一个结果参数。
msg_iov和msg_iovlen这两个成员制定输入或输出缓冲区数组(即iovec结构数组),类似readv和writev的第二个和第三参数。
msg_control和msg_controlln这两个成员指定可选的辅助数据的位置和大小。msg_controllen对于recvmsg是一个结果参数。
msg_flags:
对于recvmsg和sendmsg,必须区别msg_flags和函数中传递的参数flags的区别(msghdr结构中的msg_flags成员,传递的是引用,因为传递给函数的是该结构的地址):
(1)只有recvmsg使用msg_flags成员。recvmsg被调用时,flags参数被复制到msg_flags成员,并有内核使用其值驱动接收处理过程。内核还根据recvmsg的结果更新msg_flags成员的值。
(2)sendmsg忽略msg_flags成员,因为它直接使用flags参数驱动发送处理过程。这一点意味着如果乡镇某个sendmsg调用中设置MSG_DONTWAIT标志,那就是把flags参数设置为该值,把msg_flags成员设置为该值不起作用。
辅助数据
辅助数据可以通过调用sendmsg和recvmsg这两个函数,使用msghdr结构中的msg_control和msg_controller这两个成员发送和接收。
辅助数据由一个或多个辅助数据对象构成,每个对象以一个定义在头文件中的cmsghdr结构体开头。
struct cmsghdr{
socklen_t cmsg_len;
int cmsg_type;
};
下图展示了在一个控制缓冲区中出现2个辅助数据对象的例子。
msg_control指向第一个辅助数据对象,附属数据的长度则由msg_controllen指定。每个对象开头都是一个描述该对象的cmshhdr结构。在cmsg_type成员和实际数据之间可以有填充字节,从数据结尾出到下一个辅助数据之前也可以有填充字节。
既然由recvmsg返回的辅助数据可含有任意数目的辅助数据对象,为了对应用程序屏蔽可能出现的填充字节,头文件中定义了以下5个宏,以简化对辅助数据的处理。
#include
struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *msghg);
输入参数:指向struct msghdr结构的指针
返回:指向附属数据缓冲区的第一个附属对象的struct cmsghdr指针。如果不存在附属数据对象则返回null
struct cmsghdr *CMSG_NXTHDR(struct msghdr *msgh,
struct cmsghdr *cmsg);
输入参数:指向struct msghdr结构的指针,指向当前struct cmsghdr的指针。
返回: 下一个附属数据对象的strcut cmsghdr指针,如果没有下一个附属数据对象,在返回null
size_t CMSG_ALIGN(size_t length);
输入参数:附属数据缓冲区中对象大小
返回:包火了维护对齐所需要的额外的填充字节。
size_t CMSG_SPACE(size_t length);
输入参数:附属数据缓冲区中对象的大小
返回:计算cmghdr头结构加上附属数据大小,并包括对齐字段和可能的结尾填充字符,注意CMSG_LEN()值并不包括可能的结尾填充字符。CMSG_SPACE()对于确定所需的缓冲区尺寸是非常由用的。
注意如果在缓冲区中有多个附属数据结构,一定要同时添加多个CMSG_SPACE()宏调用来得到所需的总空间。
size_t CMSG_LEN(size_t length);
输入参数:附属数据缓冲区中的对象大小
返回:计算cmsghdr头结构加上附属数据大小,包括必要的对齐字段,这个值用设置cmsghdr对象的cmsg_len成员
void * CMSG_DATA(struct cmsghdr *cmsg);
输入参数:指向cmsghdr结构的指针。
返回:跟在头部以及填充字节后的附属数据的第一个字节(如果存在)的地址。