poundifdef
poundifdef

Reputation: 19353

Successive calls to recvfrom() loses data?

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:

  1. 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

  2. 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?)

Upvotes: 8

Views: 5921

Answers (2)

vmdm
vmdm

Reputation: 59

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.

Upvotes: 3

camh
camh

Reputation: 42418

From the recv(2) man page:

If a message is too long to fit in the supplied buffer, excess bytes may be discarded depending on the type of socket the message is received from.

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.

Upvotes: 10

Related Questions