成功select()和FD_ISSET()后读取()失败

时间:2018-04-18 19:35:16

标签: c++ linux

在Linux环境中,我有一个应用程序,它从API调用到驱动程序获取文件描述符。我使用以下函数来读取系统中卡读入的数据。大约十分之一的读取失败。我很困惑为什么,在成功选择之后,检查是否设置了read_fd,没有返回数据。

int MyClass::Read(int file_descriptor)
{
    unsigned short read_buffer[READ_BUFFER_SIZE];
    fd_set read_set;
    time_val timeout;
    int return_value = 0;
    int count = 0;
    int status = -1;

    // Initialize read file descriptor
    FD_ZERO(&read_set)

    // Add driver file descriptor
    FD_SET(file_descriptor, &read_set)

    // Set timeout
    timeout.tv_sec = 0;
    timeout.tv_usec = 10000;

    while(!count)
    {
        // Wait for data to be available to read
        return_value = select(file_descriptor + 1, &read_set, NULL, NULL, &timeout);

        // Make sure an error or a timeout didn't occur
        if (-1 == return_value)
        {
            cout << "an error occurred" << endl;
        }
        else if (0 == return_value)
        {
            cout << "a timeout occurred" << endl;
        }
        else
        {
            // If the read file descriptor is set, read in the data
            if (FD_ISSET(file_descriptor, &read_set))
            {
                count = read(file_descriptor, read_buffer, sizeof(read_buffer));

                // Double check that data was read in
                if (!count)
                {
                    cout << "read failed" << endl;
                }
                else
                {
                    // Set status to success
                    status = 0;
                }
            }
        }
    }

    return status;
}

2 个答案:

答案 0 :(得分:1)

读取的返回值为0(您的if (!count)检查)并不意味着读取失败 - 这意味着读取成功并获得了EOF。

在任何情况下,select返回文件描述符集并不意味着读取该fd不会失败 - 这意味着读取该fd将不会阻塞,并将立即返回一些内容,无论是失败还是成功。

答案 1 :(得分:0)

您没有正确使用select()。它修改了fd_set,可能还修改了time_val,所以你必须在每次循环迭代时重置它们。

此外,您没有正确处理错误。 read()出错时返回-1,断开时返回0,&gt;读取字节数为0。您没有正确处理错误和断开条件。

尝试更像这样的东西:

int MyClass::Read(int file_descriptor)
{
    unsigned short read_buffer[READ_BUFFER_SIZE];
    fd_set read_set;
    time_val timeout;
    int return_value, count;

    do
    {
        // Initialize read file descriptor
        FD_ZERO(&read_set);

        // Add driver file descriptor
        FD_SET(file_descriptor, &read_set);

        // Set timeout
        timeout.tv_sec = 0;
        timeout.tv_usec = 10000;

        // Wait for data to be available to read
        return_value = select(file_descriptor + 1, &read_set, NULL, NULL, &timeout);

        // Make sure an error or a timeout didn't occur
        if (-1 == return_value)
        {
            cout << "select failed" << endl;
            return -1;
        }

        if (0 == return_value)
        {
            cout << "select timed out" << endl;
            continue; // or return, your choice...
        }

        // select() returned > 0, so the fd_set MUST be set,
        // so no need to check it with FD_ISSET()...

        // read in the data

        count = read(file_descriptor, read_buffer, sizeof(read_buffer));

        // Double check that data was actually read in
        if (-1 == count)
        {
            cout << "read failed" << endl;
            return -1;
        }

        if (0 == count)
        {
            cout << "peer disconnected" << endl;
            return 0; // or -1, or whatever you want...
        }

        // success
        break;
    }
    while (true);

    return 0;
}