Выбор модели I/O для приложения
Семь моделей ввода/вывода WinSock в окружении оконных и консольных приложений и
сервисов, различные требования протоколов верхнего (пользовательского) уровня,
однопоточность и многопоточность, однопроцессорные хосты и многопроцессорные –
весь этот набор ставит программиста перед очень непростым выбором, какую модель
избрать для решения данных конкретных задач. Ниже перечислены наиболее очевидные
варианты, рассмотренные нами в том или ином объеме:
1. "Обычные" блокирующие сокеты – "родное" состояние любого сокета –
блокирующее, и вызов операции на нем не вернет управление, пока она не
закончится.
2. "Чисто" неблокирующие сокеты – Вызов на таких сокетах возвращает
управление немедленно, даже если операция еще будет продолжаться. Хотя
принципиально до момента возврата программа может производить некоторые
действия, этот метод требует постоянного опроса для определения момента
завершения операции – обычно с помощью select().
3. Мультиплексирование с помощью select(). Функция select() блокирует
родительский поток до тех пор, пока на одном из сокетов не произойдет некоторое
сетевое событие. Обычная практика – использование select() на неблокирующих
сокетах во избежание опроса.
4. Асинхронные сокеты – это тоже неблокирующие сокеты (WSAAsyncSelect()), за
исключением того, что не требуется производить опрос – модуль стекового
протокола посылает специальное оконное сообщение, когда на сокете произойдет
нечто интересующее программу.
5. Объекты-события – Используется WSAEventSelect(), механизм похож на метод
мультиплексирование с помощью select(), но более эффективный. Кроме того, не
стоит забывать, что select() работает с Беркли-сокетами, а WSAEventSelect()
работает в среде WinSock.
6. I/O с перекрытием – Одна из наиболее примечательных особенностей WinSock
2, которая дает возможность обобщить на сокетные операции унифицированный I/O-механизм
Win32 API и дает существенный выигрыш в производительности по сравнению с
предыдущими моделями.
7. I/O Сompletion Port – наибольший эффект от применения этой модели может
быть достигнут для серверных приложений на многопроцессорных аппаратных
платформах.
Следует отметить, что применение любого из упомянутых методов в многопоточной
среде может привести к существенным модификациям того или иного избранного
механизма I/O, так как потоки существенно влияют на его природу.
Конечно же, при выборе в первую очередь следует обратить внимание на
операционную систему, для которой пишется приложение, ибо все системы обладают
различными сетевыми характеристиками.
К примеру, Windows 9x не содержат в своем ядре механизма I/O с перекрытием, и
если даже метод работает, то только за счет эмуляции на уровне API, и сокетная
операция ReadFile() в Windows 9x будет неудачной. В UNIX механизм, аналогичный I/O
с перекрытием, отсутствует. Как было уже отмечено выше, в некоторых UNIX-like
системах введено понятие "чистых" асинхронных операций aio_*(), но они не
соотносятся с WinSock-идеологией и не так широко распространены.
Далее, хотя практически все современные UNIX-like системы поддерживают так
называемые "потоки Posix – Posix threads", API для создания многопоточных
приложений и техника программирования в UNIX и Windows различны.
Из перечисленных методов функция select() для неблокирующих сокетов считается
наименее эффективной из-за больших накладных расходов на ее исполнение, и эти
расходы растут линейно при увеличении числа обслуживаемых соединений. Вместе с
тем она хороша по причине портабельности кода, потому что все системы ее
поддерживают.
Асинхронная модель с WSAAsyncSelect() также не принадлежит к числу очень
эффективных, и она хороша для небольших объемов данных, к тому же она работает
только в оконном окружении.
Модель с WSAEventSelect() обладает несколько большей эффективностью по сравнению
с моделью WSAAsyncSelect(), плюс хорошая совместимость
с "безоконными" приложениями. Она рекомендуется для серверов, получающих до 100
запросов на соединение и их обработку. Проблема лишь в том, что можно
запустить "в дело" не более 64 объектов (в одном потоке) одновременно, и если
будет создано 1024 сокета, это потребует 16–ти потоков!
Небольшой трафик – 1-100 запросов на соединение - может практически без ущерба
обслуживаться любой из этих моделей.
Для высокопроизводительных и достаточно загруженных по количеству запросов
серверов безусловно рекомендуется модель I/O с перекрытием – ни одна прежде
рассмотренная модель не может себя с ней сравнить. Она позволяет справиться с
нагрузкой десятков тысяч запросов на соединение (при условии достаточного объема
быстродействующей оперативной памяти на сервере), и еще более производительная
модель порта завершения.
Следующие рекомендации могут быть отнесены к среде исполнения. Например, не
рекомендуется программировать блокирующие вызовы в однопоточной среде с
графическим интерфейсом (GUI), в этом случае лучше работать с асинхронными
сокетами. В клиентских программах "усиленное" использование многопоточности
редко оправдано, к тому же надо принимать во внимание вопросы синхронизации и
отладки.
Многопоточность оправдана для FTP-серверов, для веб-броузеров, позволяющих
закачку файлов в фоновом режиме, для посылки писем в email-клиенте без
прерывания работы пользователя и так далее – программа должна применять
многопоточность грамотно и уместно. Возможно также применение многопоточных
клиентских приложений для задач тестирования серверов.
Текущая версия SmallProxy: 3.6.4 от 10 марта 2009 года Скачать
| Тестовая версия SmallProxy: 3.7.1 Beta 52 от 16 ноября 2009 года Скачать