从sys/socket.h了解msghdr结构

16
我正在尝试理解sys/socket.h库中msghdr结构的以下成员:

  • struct iovec *msg_iov scatter/gather array
  • void *msg_control 辅助数据,详见下文

下面说明如下:

辅助数据由一系列成对出现的cmsghdr结构和数据数组组成。 数据数组包含辅助数据消息,而cmsghdr结构包含描述性信息,允许应用程序正确解析数据。


我假设msghdr结构包含协议头信息? 如果是这样的话...*msg_iov是请求/响应中输入/输出的参数 "向量"? *msg_control包含响应消息吗?

1个回答

12

msg_iov是一个输入/输出缓冲区数组,其长度为msg_iovlen。该数组的每个成员都包含指向数据缓冲区和缓冲区大小的指针。这是读写数据所在的位置。它允许您读写不一定在连续内存区域中的缓冲区数组。

msg_control指向大小为msg_controllen的缓冲区,其中包含有关数据包的其他信息。要读取此字段,首先需要声明一个struct cmsghdr *(我们将其称为cmhdr)。通过第一次调用CMSG_FIRSTHDR()填充它,传递msghdr结构的地址,以及通过后续每次调用CMSG_NXTHDR()传递msghdr结构的地址和cmhdr当前值。

msg_control中,您可以找到有趣的东西,例如分组的目标IP(对于多播很有用)以及IP头中TOS / DSCP字节的内容(对于自定义拥塞控制协议很有用),等等。在大多数情况下,您需要进行setsockopt调用以启用接收此数据。在给定的示例中,需要启用IP_PKTINFOIP_TOS选项。

有关详细信息,请参见cmsg(3)手册页

源IP和端口不在msg_control中,而是在msg_name中,它需要一个指向长度为msg_namelenstruct sockaddr的指针。

这是如何使用它的示例:

struct msghdr mhdr;
struct iovec iov[1];
struct cmsghdr *cmhdr;
char control[1000];
struct sockaddr_in sin;
char databuf[1500];
unsigned char tos;

mhdr.msg_name = &sin
mhdr.msg_namelen = sizeof(sin);
mhdr.msg_iov = iov;
mhdr.msg_iovlen = 1;
mhdr.msg_control = &control;
mhdr.msg_controllen = sizeof(control);
iov[0].iov_base = databuf;
iov[0].iov_len = sizeof(databuf);
memset(databuf, 0, sizeof(databuf));
if ((*len = recvmsg(sock, &mhdr, 0)) == -1) {
    perror("error on recvmsg");
    exit(1);
} else {
    cmhdr = CMSG_FIRSTHDR(&mhdr);
    while (cmhdr) {
        if (cmhdr->cmsg_level == IPPROTO_IP && cmhdr->cmsg_type == IP_TOS) {
            // read the TOS byte in the IP header
            tos = ((unsigned char *)CMSG_DATA(cmhdr))[0];
        }
        cmhdr = CMSG_NXTHDR(&mhdr, cmhdr);
    }
    printf("data read: %s, tos byte = %02X\n", databuf, tos); 
}

1
为什么是struct sockaddr_in?难道不应该是struct sockaddr吗? - Jordan Davis
2
struct sockaddr 是一个通用类型,用于获取套接字信息。对于 IPv4 套接字,具体类型是 struct sockaddr_in。对于 UNIX 域套接字,它是 struct sockaddr_un,而对于 IPv6 套接字,则是 struct sockaddr_in6 - dbush
哈哈,好的。但是你在哪里看到的啊?我根本就没有在规格说明中看到这个。 - Jordan Davis
1
man 7 ipman 7 unixman 7 ipv6,以及man 7 socket - dbush
1
好的,我现在明白了...他们真的应该把那些东西放到主要规范中,或者至少放在注释中而不是声明一个通用类型,因为你甚至都不能使用那个通用类型,对吧? - Jordan Davis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接