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

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

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

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

Ответы [ 5 ]

15 голосов
/ 30 апреля 2009

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

Основной алгоритм:

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

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

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

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

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

Какая семантика вам нужна? Если вы сделаете это:

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

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

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

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

1 голос
/ 06 мая 2009

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

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

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

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

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

0 голосов
/ 06 августа 2009

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

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

0 голосов
/ 04 мая 2009

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

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

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

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

...