8

我正在开发一个使用 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);
    

当我执行此代码时,我可以毫无问题地接收前 4 个字节。但是当我尝试获取剩余的数据时,这些数据似乎丢失了。在我的输出中,我得到了垃圾 - 它看起来像是发送者正在发送到()的下一个12 个字节的一部分。

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

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

4

2 回答 2

9

从 recv(2) 手册页:

如果消息太长而无法放入提供的缓冲区,则可能会丢弃多余的字节,具体取决于接收消息的套接字类型。

这就是发生在你身上的样子。

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

于 2009-03-24T01:07:37.787 回答
2

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.

于 2009-11-10T18:32:59.437 回答