辅助数据(ancillary data)可通过调用sendmsg和recvmsg这两个函数,使用msghdr结构中的msg_control和msg_controllen这两个成员发送和接收。
辅助数据的另一个称谓是控制信息(control information)。
辅助数据由一个或多个辅助数据对象(ancillary data object)构成,每个对象以一个定义在头文件<sys/socket.h>中的cmsghdr结构开头。
struct cmsghdr { socketlen_t cmsg_len; /* length in bytes, including this structure */ int cmsg_level; /* originating protocol */ int cmsg_type; /* protocol-specific type */ /* followed by unsigned char msg_data[] */ };
由msg_control指向的辅助数据必须为各个cmsghdr结构适当地对齐,如下所示为一种对齐方法:
union { struct cmsghdr cm; char control[CMSG_SPACE(sizeof(int))]; }control_un;
如下图所示,展示了在一个控制缓冲区中出现2个辅助数据对象的一个例子:
msg_control指向第一个辅助数据对象,辅助数据的总长度则由msg_controllen指定。每个对象开头都是一个描述该对象的cmsghdr结构。在cmsg_type成员和实际数据之间可以有填充字节,从数据结尾处到下一个辅助数据对象之前也可以有填充字节。
注意,不是所有实现都支持在单个控制缓冲区中存放多个辅助数据对象。
如下图所示,展示了通过一个UNIX域套接口传递描述字或传递凭证时所用的cmsghdr结构的格式。
既然由recvmsg返回的辅助数据可含有任意数目的辅助数据对象,为了对应用程序屏蔽可能出现的填充字节,头文件<sys/socket.h>中定义了以下5个宏,以简化对辅助数据的处理。
#include <sys/socket.h> #include <sys/param.h> /* for ALIGN macro on many implementations */ struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *mhdrptr); 返回:指向第一个cmsghdr结构的指针,无辅助数据时为NULL struct cmsghdr *CMSG_NXTHDR(struct msghdr *mhdrptr, struct cmsghdr *cmsgptr); 返回:指向下一个cmsghdr结构的指针,不再有辅助数据对象时为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); 返回:给定数据量下一个辅助数据对象总的大小
这些宏可以按照如下伪代码形式使用:
struct msghdr msg; struct cmsghdr *cmsgptr; /* fill in msg structure */ /* call recvmsg() */ for(cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) { if(cmsgptr->cmsg_level == ... && cmsgptr->cmsg_type == ...) { u_char *ptr; ptr = CMSG_DATA(cmsgptr); /* process data pointed to by ptr */ } }
CMSG_FIRSTHDR返回指向第一个辅助数据对象的指针,然而如果在msghdr结构中没有辅助数据(或者msg_control为一个空指针,或者cmsg_len小于一个cmsghdr结构的大小),那就返回一个空指针。当控制缓冲区中不再有下一个辅助数据对象时,CMSG_NXTHDR也返回一个空指针。
CMSG_LEN和CMSG_SPACE的区别在于,前者不计辅助数据对象中数据部分之后可能的填充字节,因而返回的是用于存放在cmsg_len成员中的值,后者计上结尾处可能的填充字节,因而返回的是用于为辅助对象动态分配空间的大小值。