Linux msghdr结构讲解
本文我们主要Linux msghdr这一重要的数据结构,其广泛应用于如文件描述符传递,数字证书传递等方面。
1. msghdr结构
msghdr结构一般会用于如下两个函数中:
它主要用于向一个socket发送消息,或从一个socket中接收消息。此处很重要的一个作用就是用在unix域中传递一个文件描述符。struct msghdr结构如下:
#include <sys/socket.h> struct iovec { /* Scatter/gather array items */ void *iov_base; /* Starting address */ size_t iov_len; /* Number of bytes to transfer */ }; struct msghdr { void *msg_name; /* optional address */ socklen_t msg_namelen; /* size of address */ struct iovec *msg_iov; /* scatter/gather array */ size_t msg_iovlen; /* # elements in msg_iov */ void *msg_control; /* ancillary data, see below */ size_t msg_controllen; /* ancillary data buffer len */ int msg_flags; /* flags on received message */ };
这里msg_control
指针: points to a buffer for other protocol control-related messages or miscellaneous ancillary data(指向与协议控制相关的消息或者辅助数据). 而msg_controllen
为msg_control所指向的这块缓冲的长度。
msg_control
是一个struct cmsghdr结构,下面我们会介绍。
2. cmsghdr结构
cmsghdr结构如下:
struct cmsghdr { size_t cmsg_len; /* Data byte count, including header (type is socklen_t in POSIX) */ int cmsg_level; /* Originating protocol */ int cmsg_type; /* Protocol-specific type */ /* followed by unsigned char cmsg_data[]; */ };
cmsg_level
一般为原始的协议级别,cmsg_type
为前面原始协议下的某一个子类型。要访问此辅助数据结构,一般会用到如下几个函数:
这些宏用于创建和访问控制消息(msg_control),也称为辅助数据,其并不作为socket净荷数据的一部分,净荷数据保存在msg_iov中(参见上述struct msghdr)。 这些辅助数据可能包括:
-
所收到的packet的网卡接口
-
一些不太常用的头部字段
-
一个扩展的错误描述
-
一个文件描述符集合
-
UNIX credentials
例如用辅助数据可以发送一些额外的头部字段(eg. IP options)。
要访问一系列的cmsghdr结构,我们必须使用如下这些宏,而不要直接访问:
-
CMSG_FIRSTHDR(): 返回msghdr辅助数据部分指向第一个cmsghdr的指针
-
CMSG_NXTHDR(): 返回参数中cmsghdr的下一个有效cmsghdr。当msg_control buffer中没有足够剩余的空间的时候,返回NULL
-
CMSG_ALIGN(): 给定一个长度,其会返回对齐后相应的长度。它是一个常量表达式,其一般实现如下:
#define CMSG_ALIGN(len) ( ((len)+sizeof(long)-1) & ~(sizeof(long)-1) )
-
CMSG_SPACE(): 返回辅助数据及其所传递的净荷数据的总长度。即sizeof(cmsg_len) + sizeof(cmsg_level) + sizeof(cmsg_type) + len(cmsg_data)长度进行CMSG_ALIGN后的值.
-
CMSG_DATA(): 返回cmsghdr的净荷数据部分
-
CMSG_LEN(): 返回净荷数据长度进行CMSG_ALIGN后的值,一般赋值给cmsghdr.cmsg_len。
为了创建辅助数据,首先初始化msghdr.msg_controllen字段。 在msghdr上使用CMSG_FIRSTHDR()
来获取第一个控制消息,然后使用CMSG_NXTHDR()
来获取后续的控制消息。在每一个控制消息中,使用CMSG_LEN()
来初始化cmsghdr.cmsg_len,使用CMSG_DATA()
来初始化cmsghdr.cmsg_data部分。
3. 例子
如下代码片段用于寻找辅助数据中的IP_TTL选项值:
如下代码片段在unix域socket上使用SCM_RIGHTS
传递文件句柄数组:
[参看]: