我可以从套接字上准确读取一个 UDP 数据包吗?
在Linux上使用UNIX套接字API,有什么方法可以保证我读取一个UDP数据包,并且只读取一个UDP数据包?我目前正在使用 recvmsg 从非阻塞套接字读取数据包,缓冲区大小略大于我们内部网络的 MTU。这应该确保我始终可以收到完整的 UDP 数据包,但我不确定是否可以保证每次 recvmsg 调用不会收到超过一个数据包(如果数据包很小)。
recvmsg 手册页引用了 MSG_WAITALL 选项,该选项尝试等待缓冲区被填满。我们没有使用这个,那么这是否意味着 recvmsg 在读取一个数据报后总是返回?有什么办法可以保证这一点吗?
理想情况下,我想要一个跨 UNIX 解决方案,但如果不存在,是否有 Linux 特定的解决方案?
Using UNIX socket APIs on Linux, is there any way to guarantee that I read one UDP packet, and only one UDP packet? I'm currently reading packets off a non-blocking socket using recvmsg, with a buffer size a little larger than the MTU of our internal network. This should ensure that I can always receive the full UDP packet, but I'm not sure I can guarantee that I'll never receive more than one packet per recvmsg call, if the packets are small.
The recvmsg man pages reference the MSG_WAITALL option, which attempts to wait until the buffer is filled. We're not using this, so does that imply that recvmsg will always return after one datagram is read? Is there any way to guarantee this?
Ideally I'd like a cross-UNIX solution, but if that doesn't exist is there something Linux specific?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
recvmsg
将返回一个数据包,并且它将是整个数据包(只要您提供的缓冲区足够大)。来自POSIX 文档:
“一条消息”指的是一条消息(或数据包),并且,
recvmsg
will return you one packet, and it will be the entire packet (as long as the buffer you provide it is large enough).From the POSIX documentation:
"a message" means exactly one message (or packet), and,
接受的答案并没有清楚地回答OP的问题,因为它只是顺便提到了缓冲区大小。
缓冲区足够大以容纳整个数据报非常重要。数据报最多可达 65,536 字节。当一个大数据报由于 MTU 而被分段时,它将由堆栈重新组装,您不会知道这一点,您将什么也接收不到,直到所有片段都被接收并重新组合到原始数据报中。如果您使缓冲区略大于 1 MTU(例如 1600 字节),并且对 40K 字节的传入数据报调用 recv(),则您将仅获得前 1600 字节。
The accepted answer doesn't clearly answer the OP's question, because it mentions buffer size only in passing.
It's important for your buffer to be big enough to fit one entire datagram. A datagram can be up to 65,536 bytes. When a large datagram is fragmented because of MTU it will be reassembled by the stack, you won't know about this, you will just receive nothing until all the fragments are received and put back together into the original datagram. If you make your buffer slightly bigger than one MTU, for instance 1600 bytes, and you call recv() on an incoming datagram that is 40K bytes, you will get just the first 1600 bytes.
一种选择(我说选项)是使用 pcap_next 使用 libpcap 并将其拆开查看是否是udp数据包。您可以使用以下方法执行此操作:(
借自 tcpdump)
,然后通过执行以下操作来测试 ip 结构以查看它是否是 udp 数据包:
如果失败,请继续循环(调用 pcap_next)直到获得 udp 数据包。当然,通过这种方式提取 udp 数据报会比较困难,但它确实可以让您很好地了解数据包的内部结构。请参阅 tcpdump 源代码以了解如何剥离信息以及结果。
One option (I say option) is to use pcap_next using libpcap and take it apart to see if it is a udp packet. You can do this with:
(Borrowed from tcpdump)
and then test the ip struct to see if it is a udp packet by doing:
And if this fails, keep looping (calling pcap_next) until you get your udp packet. Of course, extraction of the udp datagram is harder this way, but it does let you into the packet internals quite nicely. Refer to the tcpdump source to see how to strip info out and what comes out.