Search code examples
c++unix-socket

recv() fails to read last chunk


The function below reads incoming data perfectly but only if the last data chunk is smaller than BUFFSIZE. If the size of the last buff happens to be equal to BUFFSIZE, then the program tries to recv again in the next loop iteration and sets bytes to 1844674407379551615 (most probably integer overflow) and repeats this in an infinite loop... Why? Why isn't it 0? And why doesn't it escape the loop at this stage?

std::string getMsg (int clientFileDescriptor, int timeout_sec)
{
    std::string msg;
    msg.reserve(BUFFSIZE * 10);

    char buff[BUFFSIZE + 1];

    signal(SIGPIPE, SIG_IGN);

    //set a timeout for reading and writing
    struct timeval tv;
    tv.tv_sec = timeout_sec;
    tv.tv_usec = 0;
    setsockopt (clientFileDescriptor, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));


    while (true)
    {
        size_t bytes = recv (clientFileDescriptor, buff, BUFFSIZE, 0);

        std::cout << "bytes read: " << bytes << std::endl;

        if (0 < bytes && bytes <= BUFFSIZE)
        {
            msg.append(buff, bytes);

            //'buff' isn't full  so this was the last chunk of data 
            if (bytes < BUFFSIZE)
                break;
        }
        else if (!bytes) //EOF or socket shutdown by the client
        {
            break;
        }
        else
        {
            if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) 
                continue;
            
            //We cannot continue due to other erros (e.g. EBADF/socket not available)
            break;
        }
    }

    return msg;
}

Solution

  • Probably size_t is unsigned on your platform. So if the recv times out, recv returns -1 and bytes overflows. Your code doesn't correctly handle this case due to the overflow.

    This is just wrong:

            //'buff' isn't full  so this was the last chunk of data 
    

    It's entirely possible buff wasn't full because the last chunk of data hadn't been received yet. Tou should actually add code to detect the end of a message rather than relying on a timeout to find it for you if your application protocol supports messages of different sizes.

    Unfortunately, you haven't given us the code for the other end of the connection nor specified how your application protocol works, so I have no idea what the other side does or expects. So it's not possible to give you more useful suggestions.