9

使用 recvmsg 时,我使用MSG_TRUNC并且MSG_PEEK喜欢这样:

msgLen = recvmsg(fd, &hdr, MSG_PEEK | MSG_TRUNC)

这给了我为下一条消息分配的缓冲区大小

我的问题是如何获得我应该为msg_control标题内的字段分配的缓冲区大小

4

3 回答 3

2

根据文档,您需要为msg_control大小分配缓冲区msg_controllen。要事先知道大小,您可以像以前那样打电话recvmsg(fd, &hdr, MSG_PEEK | MSG_TRUNC)。MSG_PEEK 不会删除消息,MSG_TRUNC 将允许返回消息的大小,即使缓冲区太小。

一些解决方案:

  • recvmsg(fd, &hdr, MSG_PEEK | MSG_TRUNC)根据返回的大小调用并初始化 hdr 中的缓冲区,然后在没有标志的情况下再次调用它。
  • 如果您事先知道消息的大小,请分配一个足够大的缓冲区,然后调用 recvmsg。如果发生错误(返回 -1),检查错误代码是否消息被截断(MSG_TRUNC 或 MSG_CTRUNC)
于 2018-02-21T19:23:20.507 回答
1

我不能代表 macOS 以外的其他平台(其核心基于 FreeBSD 核心,所以在 BSD 系统中也可能没有什么不同),而且 POSIX 标准也没有帮助,因为它几乎所有细节都由协议,但在 macOS 上,UDP 套接字的默认行为recvmsg是根本不传递任何控制数据。无论您msg_control在输入上设置什么大小,它都将始终0在输出上。如果您希望接收任何控制数据,您首先必须为套接字显式启用它。

例如,如果您想知道数据包的地址、源地址和目标地址(msg_name只给您接收数据包的源地址),那么您必须这样做:

int yes = 1;
setsockopt(soc, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes));

现在您将获得 IPv4 套接字的目标地址,记录为

msghdr 结构中的 msg_control 字段指向一个缓冲区,该缓冲区包含一个 cmsghdr 结构,后跟 IP 地址。cmsghdr 字段具有以下值:

cmsg_len = sizeof(struct in_addr)
cmsg_level = IPPROTO_IP
cmsg_type = IP_RECVDSTADDR

这意味着您需要在我的系统上提供至少 16 个字节的存储空间,因为struct cmsghdr在该系统上始终是 12 个字节(四倍 32 位),而 IPv4 地址是另外 4 个字节,即 16 个字节。这个值需要使用CMSG_SPACE宏正确四舍五入,但在我的系统上,宏只确保它是 32 位的倍数,而 16 字节已经是这样的倍数,所以CMSG_SPACE(16)返回16给我。

由于我事先知道我启用了哪些选项以及我将收到哪些控制数据,因此我可以提前准确计算出所需的空间。

对于原始套接字和其他更模糊的套接字,默认情况下,某些控制数据可能始终包含在输出中,即使未显式启用,但该控制数据的大小将始终相同,并且不会随着数据包在数据包之间波动有效载荷大小。因此,一旦您知道正确的大小,您就可以依靠它不会改变的事实,至少在您启用/禁用任何选项的情况下不会改变。

如果您的控制数据缓冲区太小,则MSG_CTRUNC始终在输出中设置标志(即使您没有在输入上设置任何标志),那么您需要增加控制数据缓冲区的大小并重试(使用下一个数据包或者如果您用作MSG_PEEK输入标志,则使用相同的数据包),直到您曾经能够在没有获得MSG_CTRUNC输出标志的情况下进行该调用。最后看看msg_control字段怎么说。在输入时,它是可用的缓冲区空间量,但在输出时,它包含实际使用的确切缓冲区空间量。这是接收该套接字的所有未来数据包的控制数据所需的确切缓冲区大小,除非您更改将导致发送更多/更少控制数据的选项,然后您只需要再次检测该大小,方法与前。

对于更完整的示例,您还可以查看:
https ://stackoverflow.com/a/49308499/15809

于 2018-03-15T13:12:32.490 回答
1

恐怕您无法从 Posix.1g 套接字 API 中获得该值。不确定所有实现,但在 Linux 中不可能。您可能会注意到,辅助数据缓冲区中没有提供控制流,因此您需要自己实现它,以防您在进程之间发送大量信息。另一方面,对于常见的情况,您已经知道在编译时将收到什么(但您可能已经知道这一点)。如果您需要实现自己的控制流,请考虑到,在 Linux 中,辅助数据的行为类似于流套接字。

但是,您可以在 中获取/设置最坏情况的缓冲区长度/proc/sys/net/core/optmem_max,请参阅cmsg(3)。所以,我想你可以将它设置为一个合理的值并声明一个那么大的缓冲区。

于 2018-02-28T11:27:45.607 回答