连续调用recvfrom()会丢失数据吗?
我正在开发一个使用 UDP 的可靠文件传输程序。 (计算机网络课程。)
我的问题是这样的 - 好吧,考虑一下这种情况:
发送者有(例如)12 字节的数据要发送。 所以发送者执行这个调用:
sendto(fd, &buf, 12, 0, (struct sockaddr *)&cliaddr,sizeof(cliaddr));
这以不可靠的方式发送 12 字节数据。 该数据的前 4 个字节恰好是“消息长度”字段。 在这种情况下,前 4 个字节的值可能为 0x0000000C
接收方希望使用 recvfrom() 读取前 4 个字节。 看到段大小是12字节,它想读取剩余的8字节。 所以接收器可能看起来像这样:
/*读取段大小*/ recvfrom(sockfd,&buf,4,0,(struct sockaddr *)&cliaddr,&len); /* 做一些算术,使用 bzero() 等 */ /* 读取剩余数据 */ recvfrom(sockfd,&buf,8,0,(struct sockaddr *)&cliaddr,&len);
当我执行这段代码时,我可以毫无问题地接收前 4 个字节。 但是当我尝试获取剩余数据时,该数据似乎丢失了。 在我的输出中,我收到了垃圾信息 - 它看起来像是发送者正在 sendto() 处理的下 12 个字节的某些部分。
这是预期的行为吗? 也就是说,如果单个 recvfrom() 调用没有读取发送的所有数据,是否不能保证该数据(剩余的 8 个字节)可供我使用?
发送段标头(包括其大小),然后发送有效负载的标准方法似乎不起作用。 这是否意味着我需要发送 2 个单独的段 - 一个仅包含标头信息,然后是包含有效负载的第二个段? 或者我只是错误地使用了这些系统调用(或者是否缺少一个标志或setsockopt()?)
I am working on a reliable file transfer program that uses UDP. (for a course in computer networking.)
My question is this - well, consider this scenario:
Sender has (for example) 12 bytes of data to send. So the sender performs this call:
sendto(fd, &buf, 12, 0, (struct sockaddr *)&cliaddr,sizeof(cliaddr));
This sends the 12 bytes of data in an unreliable way. The first 4 bytes of this data happens to be a "message length" field. In this case, the first 4 bytes might have the value 0x0000000C
The receiver wants to read the first 4 bytes using recvfrom(). Seeing that the segment size is 12 bytes, it wants to read the remaining 8 bytes. So the receiver might look like this:
/* read the segment size */ recvfrom(sockfd,&buf,4,0,(struct sockaddr *)&cliaddr,&len); /* do some arithmetic, use bzero(), etc */ /* read the rest of the data */ recvfrom(sockfd,&buf,8,0,(struct sockaddr *)&cliaddr,&len);
When I execute this code, I can receive the first 4 bytes without a problem. But when I try to fetch the remaining data, that data seems to be lost. In my output, I'm getting garbage - it looks like some portion of the next 12 bytes that the sender is sendto()-ing.
Is this expected behavior? That is to say, if a single recvfrom() call does not read all of the data that was sent, is it not guaranteed that that data (the remaining 8 bytes) is available to me?
It seems like the standard method of sending a segment header (including its size), followed by the payload, does not work. Does that mean that I need to send 2 separate segments - one that only contains header information, and then a 2nd segment with the payload? Or am I just using these syscalls incorrectly (or is there a flag or setsockopt() that I'm missing?)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
另一种方法是使用 MSG_PEEK 标志进行虚拟接收。 虽然返回的大小与缓冲区大小相同(或更大),但获取更大的缓冲区并重试。
然后再次执行recvfrom(不带MSG_PEEK标志)以从UDP缓冲区中删除消息。
但当然,这是相当低效的,当您只能决定最大数据包大小时,不应该这样做。
Another method is to do a dummy recvfrom with the MSG_PEEK flag. While the returned size is the same as your buffer size (or more), get a bigger buffer and try again.
Then do recvfrom again (without the MSG_PEEK flag) to remove the message from the UDP buffer.
But of course, this is fairly inefficient and should not be done when you can just decide a maximum packet size.
来自 recv(2) 手册页:
这看起来就是发生在你身上的事情。
您应该有一个最大消息大小的缓冲区并读取该数量。 您将只读取一个数据报,并且将返回长度。 然后,您可以解析缓冲区前面的长度,并根据 recvfrom(2) 返回的内容验证它。
From the recv(2) man page:
This is what it looks like is happening to you.
You should have a buffer of the maximum message size and read that amount. You will read only one datagram and the length will be returned. You can then parse the length from the front of the buffer and validate it against what recvfrom(2) returned.