Armen Babakanian
Armen Babakanian

Reputation: 2305

Multiple read call of a socket fd with O_NONBLOCK set fails

I am using select(), and NONBLOCKING connection fd to accept connections and handle input and output. I am having some issue handling large data transfer of more than the buffer size. So for example here:

readCount = 1;
while(readCount > 0)
{
    // clear t_input to read new data from the fd
    memset(t_input, 0, CLIENT_INPUT_BUFF_LEN);

    // read data from the client connectionFd and since t_input is 1024 only
    // read that much
    readCount = read(iter->connectionFd, t_input, 1024);
    printf("<<< %d - %s >>> \n", errno, strerror(errno));

    if(readCount == -1)
    {
        if( errno == EAGAIN) 
            break;
    }

    iter->AppendInputData(t_input, readCount);
}

This doesn't work for large data. So when I transfer data that is less than 1024, The first read call successfully finishes and data is copied in the AppendInputData call. Since it's in a loop, the second read call returns -1, and set's errno to EAGAIN which then breaks the loop - all is good for this case.

However, in the case of larger than 1024 data, the second read calls fails again and errno is set to EAGAIN. The weird thing is, in debug mode, I don't see this behavior and the second or third read call returns fine and all data is collected. Can someone explain what might be happening?

Upvotes: 0

Views: 545

Answers (2)

user207421
user207421

Reputation: 310840

It's all timing. You can't assume that all the data will arrive continuously and be available whenever you call read(), or that when EAGAIN occurs that's the end of it forever. It is customary to call select() to tell you when data is available to read.

You get a different behaviour in debug mode because you're altering the timing.

Upvotes: 2

Remy Lebeau
Remy Lebeau

Reputation: 595295

Try something more like this instead:

do
{
    // read data from the client connectionFd and since t_input is 1024 only read that much
    readCount = read(iter->connectionFd, t_input, 1024);

    if (readCount == -1)
    {
        if (errno == EAGAIN) 
        {
            fd_set fd;
            FD_ZERO(&fd);
            FD_SET(iter->connectionFd, &fd);

            timeval tv;
            tv.tv_sec = 5;
            tv.tv_usec = 0;

            readCount = select(iter->connectionFd+1, &fd, NULL, NULL, &tv);
            if (readCount == 1)
                continue;

            if (readCount == 0)
            {
                printf("<<< timeout >>> \n");
                break;
            }
        }

        printf("<<< %d - %s >>> \n", errno, strerror(errno));
        break;
    }

    if (readCount == 0)
    {
        printf("<<< disconnect >>> \n");
        break;
    }

    iter->AppendInputData(t_input, readCount);
}
while (true);

Upvotes: 2

Related Questions