1 是什么?
struct msghdr 在 C 语言网络编程 是套接字消息通信的核心数据结构 是 sendmsg 和 recvmsg 这两个高级套接字 I/O 函数的关键参数。 与功能相对简单的 send 和 recv 相比, msghdr 最大的优势在于支持聚集/分散 I/O 和控制信息(辅助数据) 的传递, 例如在进程间传递文件描述符2 定义
#include<sys/socket.h>structmsghdr{void*msg_name;// 指向套接字地址结构(如 struct sockaddr)socklen_tmsg_namelen;// msg_name 的长度structiovec*msg_iov;// 指向 iovec 数组(分散/聚集 I/O)size_tmsg_iovlen;// msg_iov 数组的元素个数void*msg_control;// 指向辅助数据/控制信息缓冲区socklen_tmsg_controllen;// msg_control 缓冲区的长度intmsg_flags;// 消息标志(输入控制行为,输出返回状态)};`struct msghdr` 成员解析 `struct msghdr` 通过组合多个成员来实现其强大功能, 其主要成员可以分为四组: `msg_name` 和 `msg_namelen`: 用于指定或获取套接字地址。 `msg_name` 是一个指向 `sockaddr` 结构体的指针,`msg_namelen` 是该地址的长度。 在 `sendmsg` 中指定目标地址,在 `recvmsg` 中则可获取数据来源地址。 对于已连接的面向连接套接字(如 TCP),这些成员通常被忽略。 `msg_iov` 和 `msg_iovlen`: 实现聚集/分散 I/O。 `msg_iov` 指向一个 `struct iovec` 结构体数组,`msg_iovlen` 是数组的长度。 这允许你将数据从多个不连续的内存缓冲区发送出去,或接收到多个缓冲区中。 `msg_control` 和 `msg_controllen`: 用于处理辅助数据。 `msg_control` 指向辅助数据缓冲区,`msg_controllen` 是缓冲区大小。 辅助数据由一系列 `cmsghdr` 结构体及其附属数据构成。 `msg_flags`: 仅用于 `recvmsg` 接收特定的标志位,如 `MSG_EOR` (记录结尾)、`MSG_OOB` (带外数据) 等。3 关联数据结构
关键附加结构 要完整理解和使用 `msghdr`,还需要了解以下几个紧密关联的结构体和宏函数: `struct iovec`: 用于 `msg_iov` 成员,定义在 `<sys/uio.h>` 中, 包含 `void *iov_base` (缓冲区地址) 和 `size_t iov_len` (长度)。 `struct cmsghdr` 与辅助数据: 辅助数据由一系列 `struct cmsghdr` 构成,用于传递控制信息, 如 Unix 域套接字中的文件描述符或 IP 数据包信息等。 操作辅助数据需使用专用的宏函数,而非直接访问内存。 辅助数据专用宏函数: `CMSG_FIRSTHDR(msghdr)`: 获取 msghdr 中第一个 `cmsghdr` 结构。 `CMSG_NXTHDR(msghdr, cmsg)`: 获取下一个 `cmsghdr` 结构。 `CMSG_DATA(cmsg)`: 通过 `cmsghdr` 获取其附属数据的地址。4 struct cmsghdr 与 struct msghdr
struct cmsghdr与struct msghdr是 包含与被包含 的关系。
简单来说:struct msghdr是一次消息通信的“总信封”,
而struct cmsghdr是信封中msg_control字段所指向的“辅助数据区”里的单个控制项头部。
struct cmsghdr用于描述 单个辅助数据(Ancillary/Control Data)
`struct cmsghdr` 与 `struct msghdr` 的具体联系msghdr字段 | 与cmsghdr的关系 |
|---|---|
msg_control | 指向一块原始字节缓冲区,该缓冲区内按顺序存放一个或多个struct cmsghdr及其附属数据。 |
msg_controllen | 该缓冲区的总字节数。发送前需计算好大小,接收时由内核填入实际接收的控制数据长度。 |
| 逻辑关系 | msghdr提供“容器”,cmsghdr是容器内的“条目”。一个msg_control缓冲区可包含多个cmsghdr(如同时传递 FD 和凭证)。 |
内存布局示意:
msg_control 缓冲区: [ cmsghdr1 ][ 数据1 ][ 对齐填充 ][ cmsghdr2 ][ 数据2 ]...为什么不能直接操作指针?→ 必须使用 `CMSG_*` 宏族 由于 `cmsghdr` 之间存在**内存对齐填充**,直接 `ptr + sizeof(struct cmsghdr)` 会越界或读错数据。 POSIX 规定必须使用以下宏:| 宏 | 作用 |
|---|---|
CMSG_SPACE(len) | 计算存放长度为len的数据所需的总空间(含头部+数据+尾部对齐填充)。用于分配msg_control缓冲区。 |
CMSG_LEN(len) | 计算应写入cmsg_len的值(含头部+数据,不含尾部填充)。 |
CMSG_DATA(cmsg) | 返回指向该控制项实际数据区的指针。 |
CMSG_FIRSTHDR(msgh) | 获取msg_control缓冲区中的第一个cmsghdr。 |
CMSG_NXTHDR(msgh, cmsg) | 获取下一个cmsghdr;若已无后续项则返回NULL。 |
常见cmsg_level/cmsg_type组合
层级 (cmsg_level) | 类型 (cmsg_type) | 传递内容 |
|---|---|---|
SOL_SOCKET | SCM_RIGHTS | 文件描述符(Unix Domain Socket) |
SOL_SOCKET | SCM_CREDENTIALS | 进程 UID/GID/PID 凭证 |
IPPROTO_IP | IP_PKTINFO/IP_TTL | 数据报源接口、TTL 等 |
IPPROTO_IPV6 | IPV6_PKTINFO | IPv6 地址/接口信息 |
SOL_SOCKET | SCM_TIMESTAMPING | 硬件/软件时间戳 |
总结
| 维度 | struct msghdr | struct cmsghdr |
|---|---|---|
| 定位 | 消息通信的总控制块 | 辅助数据区的单个条目头部 |
| 关联字段 | msg_control,msg_controllen | 存在于msg_control指向的缓冲区中 |
| 操作方式 | 直接赋值地址/长度 | 必须通过CMSG_*宏安全访问 |
| 典型用途 | 统一封装地址、数据块、控制信息、标志位 | 传递 FD、凭证、IP 元数据、时间戳等内核附加信息 |
msghdr是消息的“信封”和“载体”,
而cmsghdr是信封内用来携带“控制信息”的结构化便签。
没有msghdr提供的辅助数据缓冲区,cmsghdr就无处安放;
没有cmsghdr对控制信息的标准化封装,msghdr就无法传递文件描述符、IP 选项等高级内容。
msghdr是外壳,cmsghdr是内件;msg_control是字节流,CMSG_*是解析器。
在编写高性能网络服务、容器进程间通信或底层抓包工具时,
这套机制是传递元数据和特殊资源的唯一标准途径。