sendmsg 和 recvmsg 中的 iov 和 msg_control
iov and msg_control in sendmsg and recvmsg
iov.iov_base 和 msg.msg_control 和有什么不一样?
我正在查看一些代码示例(ipuitls 开源 ping)
使用 sendmsg 发送数据时,数据包设置在 iov.iov_base
当使用 recvmsg 读取数据时,数据包直接从 msg->msg_control 中读取。
struct iovec 和 struct msghdr 有什么关系? reading/sending 数据有区别吗?
抱歉这个愚蠢的问题。到目前为止我还没有找到答案,我很困惑。
谢谢!
辅助数据或控制消息(.msg_control
处的 .msg_controllen
字节)是由内核提供或验证的数据,而正常有效负载(在 iovec
中)只是接收到的数据来自另一个端点,未经内核验证和检查(校验和除外,如果协议有的话)。
对于 IP 套接字(参见 man 7 ip),有几个套接字选项可以使内核提供有关接收到的消息的辅助数据。例如:
IP_RECVORIGDSTADDR
socket 选项告诉内核提供一个 IP_ORIGDSTADDR
类型的辅助消息(以 struct sockaddr_in
作为数据),标识套接字的原始目标地址收到数据报
IP_RECVOPTS
套接字选项告诉内核提供一个 IP_OPTIONS
类型的辅助消息,其中包含所有 IP 选项 headers (IPv4 最多 40 个字节)用于传入数据报
Ping 和 traceroute 在 IP 上使用 ICMP 消息;有关详细信息,请参阅 man 7 icmp (and man 7 raw)。
因为大多数 ICMP 响应不包含发件人填写的有用数据,所以 iovec
s 通常不包含任何有趣的内容。相反,有趣的数据在 IP 消息 headers 和选项中。
例如,一个ICMP Echo reply数据包只包含8个字节(64位):8位类型(0),8位代码(0),16位校验和,16位id,和 16 位序列号。要获得具有有趣字段的 IP headers,您需要内核将它们作为辅助数据控制消息提供。
背景:
如 sendmsg() 和相关手册页所述,我们有
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
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 */
size_t msg_controllen; /* Ancillary data buffer len */
int msg_flags; /* Flags (unused) */
};
struct iovec {
void *iov_base; /* Starting address */
size_t iov_len; /* Number of bytes to transfer */
};
其中 man 3 cmsg 描述了如何构建和访问此类辅助数据,
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 */
unsigned char cmsg_data[]; /* Data itself */
};
struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *msgh);
struct cmsghdr *CMSG_NXTHDR(struct msghdr *msgh, struct cmsghdr *cmsg);
size_t CMSG_ALIGN(size_t length);
size_t CMSG_SPACE(size_t length);
size_t CMSG_LEN(size_t length);
unsigned char *CMSG_DATA(struct cmsghdr *cmsg);
这些辅助数据消息总是与当前架构充分对齐(以便可以直接访问数据项),因此要构建适当的辅助消息(SCM_CREDENTIALS 以传递用户、组和进程通过 Unix 域套接字的 ID 信息,或 SCM_RIGHTS 传递文件描述符),必须使用这些宏。 man 3 cmsg 手册页包含这些的示例代码。
可以这么说,要遍历给定消息 (struct msghdr msg
) 中的每个辅助数据部分,您使用的东西可以归结为
char *const end = (char *)msg.msg_control + msg.msg_controllen;
char *ptr = (char *)msg.msg_control;
for (char *ptr = (char *)msg.msg_control; ptr < end;
ptr += ((struct cmsghdr *)ptr)->cmsg_len) {
struct cmsghdr *const cmsg = (struct cmsghdr *)ptr;
/* level is cmsg->cmsg_level and type is cmsg->cmsg_type, and
cmsg->cmsg_data is sufficiently aligned for the level and type,
so you can use ((datatype *)(cmsg->cmsg_data)) to obtain a pointer
to the type corresponding to this level and type ancillary payload.
The exact size of the payload is
(cmsg->cmsg_len - sizeof (struct cmsghdr))
so e.g. an SCM_RIGHTS ancillary message, with
cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS
has exactly
(cmsg->cmsg_len - sizeof (struct cmsghrd)) / sizeof (int)
new file descriptors as a payload.
*/
}
iov.iov_base 和 msg.msg_control 和有什么不一样? 我正在查看一些代码示例(ipuitls 开源 ping) 使用 sendmsg 发送数据时,数据包设置在 iov.iov_base 当使用 recvmsg 读取数据时,数据包直接从 msg->msg_control 中读取。
struct iovec 和 struct msghdr 有什么关系? reading/sending 数据有区别吗?
抱歉这个愚蠢的问题。到目前为止我还没有找到答案,我很困惑。 谢谢!
辅助数据或控制消息(.msg_control
处的 .msg_controllen
字节)是由内核提供或验证的数据,而正常有效负载(在 iovec
中)只是接收到的数据来自另一个端点,未经内核验证和检查(校验和除外,如果协议有的话)。
对于 IP 套接字(参见 man 7 ip),有几个套接字选项可以使内核提供有关接收到的消息的辅助数据。例如:
IP_RECVORIGDSTADDR
socket 选项告诉内核提供一个IP_ORIGDSTADDR
类型的辅助消息(以struct sockaddr_in
作为数据),标识套接字的原始目标地址收到数据报IP_RECVOPTS
套接字选项告诉内核提供一个IP_OPTIONS
类型的辅助消息,其中包含所有 IP 选项 headers (IPv4 最多 40 个字节)用于传入数据报
Ping 和 traceroute 在 IP 上使用 ICMP 消息;有关详细信息,请参阅 man 7 icmp (and man 7 raw)。
因为大多数 ICMP 响应不包含发件人填写的有用数据,所以 iovec
s 通常不包含任何有趣的内容。相反,有趣的数据在 IP 消息 headers 和选项中。
例如,一个ICMP Echo reply数据包只包含8个字节(64位):8位类型(0),8位代码(0),16位校验和,16位id,和 16 位序列号。要获得具有有趣字段的 IP headers,您需要内核将它们作为辅助数据控制消息提供。
背景:
如 sendmsg() 和相关手册页所述,我们有
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
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 */
size_t msg_controllen; /* Ancillary data buffer len */
int msg_flags; /* Flags (unused) */
};
struct iovec {
void *iov_base; /* Starting address */
size_t iov_len; /* Number of bytes to transfer */
};
其中 man 3 cmsg 描述了如何构建和访问此类辅助数据,
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 */
unsigned char cmsg_data[]; /* Data itself */
};
struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *msgh);
struct cmsghdr *CMSG_NXTHDR(struct msghdr *msgh, struct cmsghdr *cmsg);
size_t CMSG_ALIGN(size_t length);
size_t CMSG_SPACE(size_t length);
size_t CMSG_LEN(size_t length);
unsigned char *CMSG_DATA(struct cmsghdr *cmsg);
这些辅助数据消息总是与当前架构充分对齐(以便可以直接访问数据项),因此要构建适当的辅助消息(SCM_CREDENTIALS 以传递用户、组和进程通过 Unix 域套接字的 ID 信息,或 SCM_RIGHTS 传递文件描述符),必须使用这些宏。 man 3 cmsg 手册页包含这些的示例代码。
可以这么说,要遍历给定消息 (struct msghdr msg
) 中的每个辅助数据部分,您使用的东西可以归结为
char *const end = (char *)msg.msg_control + msg.msg_controllen;
char *ptr = (char *)msg.msg_control;
for (char *ptr = (char *)msg.msg_control; ptr < end;
ptr += ((struct cmsghdr *)ptr)->cmsg_len) {
struct cmsghdr *const cmsg = (struct cmsghdr *)ptr;
/* level is cmsg->cmsg_level and type is cmsg->cmsg_type, and
cmsg->cmsg_data is sufficiently aligned for the level and type,
so you can use ((datatype *)(cmsg->cmsg_data)) to obtain a pointer
to the type corresponding to this level and type ancillary payload.
The exact size of the payload is
(cmsg->cmsg_len - sizeof (struct cmsghdr))
so e.g. an SCM_RIGHTS ancillary message, with
cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS
has exactly
(cmsg->cmsg_len - sizeof (struct cmsghrd)) / sizeof (int)
new file descriptors as a payload.
*/
}