我正在开发一个使用UDP的可靠文件传输程序。 (用于计算机网络课程。)
我的问题是 - 好吧,请考虑这种情况:
发件人(例如)要发送12个字节的数据。所以发件人执行此调用:
sendto(fd, &buf, 12, 0, (struct sockaddr *)&cliaddr,sizeof(cliaddr));
这以不可靠的方式发送12个字节的数据。该数据的前4个字节恰好是“消息长度”字段。在这种情况下,前4个字节的值可能为0x0000000C
接收方希望使用recvfrom()读取前4个字节。看到段大小是12个字节,它想要读取剩余的8个字节。所以接收器可能看起来像这样:
/* 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);
当我执行此代码时,我可以毫无问题地收到前4个字节。但是当我尝试获取剩余数据时,这些数据似乎丢失了。在我的输出中,我得到垃圾 - 它看起来像 next 12字节的一部分发送者发送到() - 。
这是预期的行为吗?也就是说,如果单个recvfrom()调用没有读取所有已发送的数据,是否可以保证该数据(剩余的8个字节)可供我使用?
似乎发送段头(包括其大小)的标准方法(后跟有效负载)不起作用。这是否意味着我需要发送2个单独的段 - 一个只包含头信息,然后是第二个带有效载荷的段?或者我只是错误地使用这些系统调用(或者是否存在我缺少的标志或setsockopt()?)
答案 0 :(得分:8)
来自recv(2)手册页:
如果邮件太长而无法容纳 提供缓冲区,多余的字节可能 根据类型丢弃 socket收到消息。
这就是发生在你身上的事情。
您应该拥有最大邮件大小的缓冲区并读取该数量。您将只读取一个数据报,并返回长度。然后,您可以从缓冲区的前面解析长度,并根据recvfrom(2)返回的内容进行验证。
答案 1 :(得分:2)
另一种方法是使用MSG_PEEK标志进行虚拟recvfrom。虽然返回的大小与缓冲区大小(或更多)相同,但请获取更大的缓冲区并重试。 然后再次recvfrom(没有MSG_PEEK标志)从UDP缓冲区中删除消息。
但是,当然,这是相当低效的,当您可以决定最大数据包大小时,不应该这样做。