C++ неблокирующее чтение

Все, что мне нужно сделать, это просто прочитать все доступные байты из сокета. Но есть одно условие: есть метод, который читает n байт из неблокирующего сокета, который я должен использовать для реализации другого метода, который будет читать все доступные байты.

Неблокирующее чтение некоторых данных определенного размера:

ssize_t read(void* buf, size_t len)
{
    ssize_t bytesRead = 0;
    ssize_t bytesTotallyRead = 0;
    size_t bytesLeftToRead = len;
    while (bytesTotallyRead < len)
    {
        bytesRead = ::recv(handle, (char*)buf+bytesTotallyRead, bytesLeftToRead, 0);
        if (bytesRead > 0)
        {
            bytesTotallyRead += bytesRead;
            bytesLeftToRead -= bytesRead;
        }
        else if (bytesRead == 0)
        {
            break;
        }
        else if (bytesRead < 0)
        {
            if (errno == EINTR)
            {
                continue;
            }
            else if (errno == EWOULDBLOCK)
            {
                int selectStatus(waitForIncomingData(500));
                if (selectStatus > 0)
                {
                    continue;
                }
                else
                {
                    break;
                }
            }
            else
            {
                break;
            }
        }
    }
    return (bytesRead==-1)?-1:bytesTotallyRead;
}

Поэтому я должен использовать этот метод для реализации чего-то вроде ssize_t readAllAvailable(std::vector<uint8_t> &data), который использует метод выше для его реализации.

Проблема здесь, как вы видите, в том, что read() использует select() для ожидания входящих данных, поэтому использование буфера фиксированного размера (в цикле) может привести к ожиданию 500 мс (в этом конкретном случае) после 1-й итерации, и это определенно не так. это должно быть сделано.

Это вообще возможно? Я имею в виду некоторые эффективные способы.


person slowcheetah    schedule 28.03.2017    source источник
comment
Если вам нужно подождать EWOULDBLOCK, это не неблокирующий и не асинхронный режим.   -  person Daniel Kamil Kozar    schedule 29.03.2017
comment
NB «Неблокирующий» и «асинхронный» — это не одно и то же, и этот код не является ни тем, ни другим.   -  person user207421    schedule 29.03.2017


Ответы (1)


есть метод, который асинхронно читает n байтов из сокета

Нет, нет. Существует метод, который использует неблокирующий режим и select() для повторной реализации блокирующего режима. В этом нет ничего «асинхронного». С точки зрения вызывающего абонента это блокирующий режим. Оно плохо, даже неправильно, задумано и должно быть отброшено. Код, который вы разместили, полностью и совершенно бессмысленный. Это просто сложный способ реализации блокирующего цикла чтения. Используйте режим блокировки. По сути, вы уже есть, но с большим количеством системных вызовов. И потеряйте вызов select() и прочее EAGAIN/EWOUDLBLOCK.

который я должен использовать для реализации другого метода, который будет читать все доступные байты. ... Проблема здесь, как вы видите, в том, что read() использует select() для ожидания входящих данных, поэтому использование буфера фиксированного размера (в цикле) может привести к 500 мс (в этом конкретном случае) ожидания после 1-й итерации

Нет, это не так. select() вернет сразу же, как данные станут доступными в сокете. Нет 500 мс, если нет данных. И «использование буфера фиксированного размера» тут ни при чем.

и это определенно не так, как это должно быть сделано.

Это не так, как это делается.

Здесь нет проблемы, которую нужно решить.

person user207421    schedule 28.03.2017
comment
Итак, как лучше всего прочитать все доступные байты в режиме блокировки? Используя ioctl (FIONREAD), выберите (epoll недоступен в некоторых ОС) или что? А что, если я отправлю запрос, затем дождусь ответа, но буфер ОС все еще пуст (потому что сервер еще не отправил все байты), а ioctl(FIONREAD) возвращает 0? Как это должно быть правильно реализовано в режиме блокировки с минимальными системными вызовами? - person slowcheetah; 29.03.2017
comment
Вам не нужно ничего больше, чем recv(). Он будет блокироваться до тех пор, пока не будет доступен хотя бы один байт, или EOS, или ошибка, в противном случае будет возвращено столько байтов, сколько доступно. Как говорится в документации. Вам не нужно ioctl(), select(), epoll() и т.д. - person user207421; 29.03.2017
comment
Да, но я не знаю точного размера входящих данных (я получаю текстовый ответ, анализирую его и т. д.). Поэтому я не могу просто читать фиксированные байты, потому что их не может быть ни слишком много, ни просто недостаточно. И да, я знаю, что в этом случае нет ничего лишнего, потому что он просто прочитает все доступные данные, но в моем случае размер сообщений сервера может составлять десятки мегабайт. - person slowcheetah; 29.03.2017
comment
Так для чего тогда нужен параметр len? Код, который вы разместили, читает до len байт. То же самое можно сказать и о неблокирующем recv() с len в качестве третьего параметра и буфером подходящего размера в качестве второго параметра. И вы заявили, что «единственной проблемой» с этим кодом было несуществующее ожидание в 500 мс. Так в чем именно в чем проблема? - person user207421; 29.03.2017
comment
После того, как вы разместили свой комментарий, мы начали обсуждать другую проблему. Мне все еще нужно прочитать все доступные данные из сокета, но в моем случае это лучше сделать в режиме блокировки (и спасибо за совет). Итак, теперь мне нужно сделать что-то вроде ssize_t readAllAvailable(std::vector<uint8_t> &data). Потому что я не знаю размер входящего сообщения, а он разный (от десятков байт до десятков мегабайт). Итак, мой вопрос: как лучше всего прочитать все доступные байты в режиме блокировки? (Думаю, мне нужно вызвать select, когда ioctl(finoread), а затем прочитать фиксированный фрагмент данных через read). - person slowcheetah; 29.03.2017
comment
Дайте определение «доступен». Протокол вашего приложения должен так или иначе сообщать вам, что должно быть прочитано в каждой точке, либо через префикс длина-слово, либо через систему тип-длина-значение, либо через протокол с самоописанием, такой как XML, Java Serialization, .. . - person user207421; 29.03.2017
comment
Это что-то вроде IMAP: вы отправляете какой-то запрос, а ответ содержит текстовые строки. EOF ответа — это строка (с '\n' в конце) с каким-то специальным префиксом или чем-то в этом роде. Так что после отправки запроса можете дать 100% ответ будет. - person slowcheetah; 29.03.2017