我想接收任意大小的整个 UDP 数据报并将字节放入缓冲区。但是,在分配缓冲区之前,我需要找出数据报的大小。不幸的是,我在任何地方都找不到这样做的例子,StackOverflow 上的任何答案似乎都没有完全解决这个问题。
我能想到的最好的办法是:
sockaddr_in senderAddress;
memset(&senderAddress, 0, sizeof(senderAddress));
socklen_t senderAddressLen = sizeof(senderAddress);
ssize_t datagramLength = recvfrom(udpSocket, nullptr, 0, MSG_PEEK, reinterpret_cast<sockaddr*>(&senderAddress), &senderAddressLen);
不幸的是,这总是返回零,即使
recvfrom()
具有实际提供的缓冲区将返回非零字节。
我想到,也许
recvfrom()
的长度参数可能是问题所在,但将其更改为更大的值只会导致 recvfrom()
返回错误,特别是“错误地址”。这可能是因为传递的数据指针为空。
还有其他可能的方法可以做到这一点,但它们在不同系统中返回的内容似乎不一致。考虑这里最后一条评论。
还有一个问题有人问过这个问题(接收整个UDP数据报,无论大小?),但解释非常简短,即使我包括
MSG_TRUNC
,它对我也不起作用。
另一个问题(know udp packet size with poll select or epoll)提到使用
MSG_PEEK
,但它并没有完全解决问题,而且似乎假设你想偷看数据。
我其实不想看数据。我只想找出长度,然后分配一个缓冲区,然后将数据读入其中。有办法做到这一点吗?
谢谢!
注意:虽然我计划在 Linux 上运行它,但我是在 Mac 上进行原型设计。
MSG_TRUNC
似乎已经定义了,但似乎没有做任何事情。
在将 UDP 数据报读入缓冲区之前找出其大小?
POSIX 没有定义任何方法来做到这一点。您使用
ioctl()
展示并介绍了潜在的技术,但是 POSIX 没有定义除 STREAMS 文件之外的任何 ioctl(不要与 C 流或面向流的套接字协议混淆)。您还提到了一种涉及将 MSG_TRUNC
标志传递给 recv()
或 recvfrom()
的技术,但是
MSG_TRUNC
没有被 POSIX 记录,并且recv()
recvfrom()
完全接受它,您应该能够将 MSG_TRUNC
与 MSG_PEEK
结合起来以避免消耗消息,这样您就可以进行第二次往返以将整个消息接收到足够大的缓冲区。这适用于 Linux,但不一定适用于其他 POSIX 平台。
接收任意大小的整个UDP数据报
,这并不是什么东西。
最坏的情况下,UDP 协议能够表示的最大有效负载大小略小于 64 KiB,因此如果您不想施加任何其他限制,那么您至少可以依赖它。
在分配缓冲区之前,我需要找出数据报的大小
如果“分配”是指“动态”分配,那么您可能最好使用足够大的自动数组来代替。其中“足够大”意味着足以容纳应用程序支持的最大数据报,该数据报不能超过 64 KiB,但可能会限制为更小的数据报。在某些情况下,您可能想要创建动态分配的副本,但即使如此,额外的分配和副本可能仍然优于额外的系统调用。
注意:虽然我计划在 Linux 上运行它,但我是在 Mac 上进行原型设计。 MSG_TRUNC
如果您想要同时在 Mac 和 Linux 上运行的东西,那么您可能确实希望将自己限制在 POSIX 指定的功能上。这很大程度上使您处于“[没有]定义[d]方式来执行此操作”类别。如上所述,最好的选择是将数据报接收到足够大的缓冲区中,以容纳远程对等方可以发送的任何有效消息。