Как я могу буферизировать неблокирующий ввод-вывод?

StackOverflow https://stackoverflow.com/questions/637964

  •  10-07-2019
  •  | 
  •  

Вопрос

Когда мне нужен буферизованный ввод-вывод для блокирующего файлового дескриптора, я использую stdio.Но если я переведу файловый дескриптор в неблокирующий режим в соответствии с ручным stdio, буферизация станет непригодной для использования.После некоторых исследований я вижу, что BIO можно использовать для буферизации неблокирующего ввода-вывода.

Но, может быть, есть и другие альтернативы?

Мне это нужно, чтобы избежать использования потоков в среде с несколькими подключениями.

Это было полезно?

Решение

Я думаю, то, о чем вы говорите, - это Схема реактора.Это довольно стандартный способ обработки большого количества сетевых подключений без потоков, и он очень распространен в серверных движках многопользовательских игр.Другой реализацией (на python) является скрученная матрица.

Основным алгоритмом является:

  • имейте буфер для каждого сокета
  • проверьте, какие сокеты готовы к чтению (select(), poll() или просто повторите)
  • для каждого гнезда:
    • вызовите recv() и накапливайте содержимое в буфере сокета до тех пор, пока recv не вернет 0 или не выдаст ошибку с EWOULDBLOCK
    • вызовите обработчик данных прикладного уровня для сокета с содержимым буфера
    • очистите буфер сокета

Другие советы

Я вижу, что теперь вопрос отредактирован и, по крайней мере, более понятен, чем раньше.

В любом случае, разве это не противоречие?

  • Вы делаете ввод-вывод неблокирующим, потому что хотите иметь возможность быстро считывать небольшие объемы данных, обычно жертвуя пропускной способностью из-за задержки.
  • Вы делаете его буферизованным, потому что вас не так уж сильно волнует задержка, но вы хотите эффективно использовать подсистему ввода-вывода, обменивая задержку на пропускную способность.

Выполнение их обоих одновременно кажется противоречием, и его трудно себе представить.

Какую семантику вы ищете?Если ты сделаешь это:

int     fd;
char    buf[1024];
ssize_t got;

fd = setup_non_blocking_io(...);
got = read(fd, buf, sizeof buf);

Какого поведения вы ожидаете, если доступно 3 байта?Блокирующий / буферизованный ввод-вывод может блокироваться до тех пор, пока не удастся прочитать больше, удовлетворяя ваш запрос, неблокирующий ввод-вывод вернет 3 доступных байта немедленно.

Конечно, если у вас есть какой-то протокол сверху, который определяет некоторую структуру сообщения, чтобы вы могли знать, что "этот ввод-вывод неполный, я не могу разобрать его, пока у меня не будет больше данных", вы можете сами буферизировать его на этом уровне и не передавать данные вверх, пока не будет получено полное сообщение.

В зависимости от протокола, вполне возможно, что вам потребуется буферизовать чтение для неблокирующего сетевого узла (клиента или сервера).

Как правило, эти буферы предоставляют несколько индексов (смещений), которые одновременно записывают позицию последнего обработанного байта и последнего считанного байта (который либо совпадает, либо превышает обработанное смещение). И они также (должны) обеспечивать более богатую семантику сжатия буфера, прозрачного управления размером буфера и т. Д.

В Java (по крайней мере) пакеты неблокирующих сетевых io (NIO) также предоставляют набор структур данных (ByteBuffer и т. д.), которые направлены на предоставление общей структуры данных.

Либо существуют такие структуры данных для C, либо вы должны свернуть свои собственные. Если у вас есть данные, просто прочитайте столько данных, сколько доступно, и дайте буферу решить такие проблемы, как переполнение (например, чтение байтов через границы кадра сообщения), и используйте смещение маркера, чтобы пометить байты, которые вы обработали.

Как указывало в Android, вам (очень вероятно) нужно будет создавать согласованные буферы для каждого открытого соединения.

Вы можете создать структуру с буферами для каждого дескриптора открытого файла, а затем накапливать эти буферы до тех пор, пока recv () не вернет 0 или пока у вас не будет достаточно данных для обработки в буфере.

Если я правильно понимаю ваш вопрос, вы не сможете буферизовать, потому что, не блокируя, вы пишете в один и тот же буфер с несколькими подключениями (если он глобальный) или просто пишете небольшие фрагменты данных (если он локальный).

В любом случае ваша программа должна быть в состоянии определить, откуда поступают данные (возможно, по файловому дескриптору), и соответствующим образом буферизировать их.

Потоки - это тоже опция, это не так страшно, как многие заставляют это казаться.

Библиотека evcom Райана Даля , которая делает именно то, что вы хотели.

Я использую его в своей работе, и он отлично работает. Имейте в виду, однако, что у него (пока, но скоро) не будет асинхронного разрешения DNS. Райан предлагает udns Майкла Токарева для этого. Я пытаюсь использовать udns вместо блокировки getaddrinfo ().

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top