Results 1 to 6 of 6

Thread: How to ensure recv() to have all data send() in tcp

  1. #1
    Join Date
    Aug 2013
    Beans
    10

    Post How to ensure recv() to have all data send() in tcp

    I am implementing the recvall() function to be sure that the data is completely sent. Also I modified the send() function to sendall() like this:

    int sendall (int consocket, char* buf, int* len) {
    int total = 0;
    int bytesleft = *len; // how many we have left to send
    int n;
    while(total < *len) {
    n = send(consocket, buf+total, bytesleft, 0);
    if (n == -1) { break; }
    total += n;
    bytesleft -= n;
    *len = total; // return number actually sent here
    return n==-1?-1:0; // return -1 on failure, 0 on success
    }
    }

    How can I implement recvall()? Say I sent from the server a struct of 14 bytes and I check in the client and get 12 bytes.. now in an unreliable situation how should I manage to get the other two bytes... I have spent time trying... any help welcomed
    Last edited by Javed_iqbal; October 19th, 2013 at 01:01 PM.

  2. #2
    Join Date
    Nov 2007
    Location
    London, England
    Beans
    7,701

    Re: How to ensure recv() to have all data send() in tcp

    You absolutely have to impose some extra structure on the data you send to do this. TCP offers just a byte stream - guaranteed to be in order, but no timing guarantees. There are two common methods for doing this.

    The first is to send a message at the start of the structure you are sending telling the recipient how big the following payload is. HTTP uses this for instance, by sending a Content-Length line in its header. The disadvantage of this is that the sender has to know the payload size in advance. Streaming an unknown-length payload can be achieved by sending "chunks" and indicating in the header whether or not each chunk is the final one. I have read about an implementation that simply sends the number of bytes in decimal followed by an end-of-line character as the header, and this may well be the easiest thing for you to do, e.g. send "14\n" followed by your 14-byte structure.

    The second method is to put a recognisable end-of-message marker on the end of the transmission. The disadvantage of this is that the payload must not be able to contain this marker, so either you have to know the payload structure, or you have to encode and decode the payload during transfer using a scheme designed to alter any occurrences of the marker in the payload. UUCP does this by encoding binary data into ASCII and using extra characters to then indicate the end. HTTP headers do it by using a blank line as the end of header marker, but requiring that the header cannot contain blank lines.

    Since you are writing your own sendall and receiveall routines, it might make sense to do the header or trailer handling inside them, hiding the ugly details from the caller.

    P.S. I just found this nice description of the problem:
    http://brunov.info/blog/2013/02/09/t...ring-messages/
    Last edited by The Cog; October 19th, 2013 at 02:05 PM.

  3. #3
    Join Date
    Aug 2013
    Beans
    10

    Re: How to ensure recv() to have all data send() in tcp

    will select() serve the same?

  4. #4
    Join Date
    Aug 2013
    Beans
    10

    Re: How to ensure recv() to have all data send() in tcp

    will using select in an indefinite loop serve the purpose?

  5. #5
    Join Date
    Jun 2007
    Location
    Maryland, US
    Beans
    6,288
    Distro
    Kubuntu

    Re: How to ensure recv() to have all data send() in tcp

    Quote Originally Posted by Javed_iqbal View Post
    will using select in an indefinite loop serve the purpose?
    IMHO, you should avoid blocking on a recv() call unless you know that there is data ready to be read. Thus, yes, select() would help to notify you that there is activity (data to be read, a disconnect, etc) on the socket. It is also helpful in cases where multiple sockets need to be handled.

    Your sendall() function could probably be simplified to the following:
    Code:
    int sendall(int sock, const char* data, int data_length)
    {
        int bytes_sent = 0;
    
        while (bytes_sent < data_length)
        {
            int result = send(sock, data + bytes_sent, data_length - bytes_sent, 0);
    
            if (result == -1)
            {
                if (errno == EAGAIN) continue;   // optionally, you may also want to check if errno == EINTR
    
                bytes_sent = -1;
            }
            bytes_sent += result;
        }
    
        return bytes_sent;
    }

  6. #6
    Join Date
    Nov 2007
    Location
    London, England
    Beans
    7,701

    Re: How to ensure recv() to have all data send() in tcp

    Using select() will help you know when there is some data to recieve, but it will not help you know when a complete message has been received. TCP delivers a byte stream - it does not and cannot convey any structure or indication of separate records etc. If you need that structure then it must be embedded into the stream somehow. There is no point wriggling and looking for other answers. That's what has to be done. With the examples you posted, I would suggest doing as my earlier post, send a \n terminated record length string before the payload itself.

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •