tags:

views:

528

answers:

2

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

+5  A: 

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.

camh
That is to say, the maximum segment size must be fixed, and known in advance by both the client and the server?
rascher
The maximum size of a UDP datagram is 64KiB. You may not be able to send this much, depending on the send buffer size. (MSS is a TCP term, BTW).
camh
Since you are designing this protocol, feel free to pick a maximum size. UNP Vol1, 3rd Edition recommends using a buffer that is one byte greater than the largest message you ever expect to receive. If recvfrom() returns a value equal to the length of your buffer, it should be treated as an error.
sigjuice
+1  A: 

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.

lillehenke