连续调用recvfrom()会丢失数据?

时间:2009-03-24 01:03:18

标签: c sockets udp

我正在开发一个使用UDP的可靠文件传输程序。 (用于计算机网络课程。)

我的问题是 - 好吧,请考虑这种情况:

  1. 发件人(例如)要发送12个字节的数据。所以发件人执行此调用:

    sendto(fd, &buf, 12, 0, (struct sockaddr *)&cliaddr,sizeof(cliaddr));
    

    这以不可靠的方式发送12个字节的数据。该数据的前4个字节恰好是“消息长度”字段。在这种情况下,前4个字节的值可能为0x0000000C

  2. 接收方希望使用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);
    
  3. 当我执行此代码时,我可以毫无问题地收到前4个字节。但是当我尝试获取剩余数据时,这些数据似乎丢失了。在我的输出中,我得到垃圾 - 它看起来像 next 12字节的一部分发送者发送到() - 。

    这是预期的行为吗?也就是说,如果单个recvfrom()调用没有读取所有已发送的数据,是否可以保证该数据(剩余的8个字节)可供我使用?

    似乎发送段头(包括其大小)的标准方法(后跟有效负载)不起作用。这是否意味着我需要发送2个单独的段 - 一个只包含头信息,然后是第二个带有效载荷的段?或者我只是错误地使用这些系统调用(或者是否存在我缺少的标志或setsockopt()?)

2 个答案:

答案 0 :(得分:8)

来自recv(2)手册页:

  

如果邮件太长而无法容纳   提供缓冲区,多余的字节可能   根据类型丢弃   socket收到消息。

这就是发生在你身上的事情。

您应该拥有最大邮件大小的缓冲区并读取该数量。您将只读取一个数据报,并返回长度。然后,您可以从缓冲区的前面解析长度,并根据recvfrom(2)返回的内容进行验证。

答案 1 :(得分:2)

另一种方法是使用MSG_PEEK标志进行虚拟recvfrom。虽然返回的大小与缓冲区大小(或更多)相同,但请获取更大的缓冲区并重试。 然后再次recvfrom(没有MSG_PEEK标志)从UDP缓冲区中删除消息。

但是,当然,这是相当低效的,当您可以决定最大数据包大小时,不应该这样做。