Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
33
Добавлен:
05.06.2015
Размер:
282.18 Кб
Скачать

Модель порта завершения

Последняя модель ввода/вывода, которую мы рассмотрим, это модель "порта завершения" – completion port. Порт завершения представляет собой специальный механизм в составе ОС, с помощью которого приложение использует объединение (пул) нескольких потоков, предназначенных единственно для цели обработки асинхронных операций ввода/вывода с перекрытием.

Приложения, которые вынуждены обрабатывать многочисленные асинхронные запросы (речь идет о сотнях и тысячах одновременно поступающих запросах – например, на поисковых серверах или популярных серверах типа www.microsoft.com), с помощью этого механизма могут обрабатывать I/O- запросы существенно быстрее и эффективнее, чем просто запускать новый поток для обработки поступившего запроса. Поддержка этого механизма включена в Windows NT, Windows 2000, Windows XP и Windows Server 2003 и особенно эффективна для мультипроцессорных систем. Так, демонстрационный программный код, который опубликован в MSDN, рассчитан на 16-ти процессорную аппаратную платформу.

Для функционирования этой модели необходимо создание специального программного объекта ядра системы, который и был назван "порт завершения". Это осуществляется с помощью функции CreateIoCompletionPort(), которая асссоциирует этот объект с одним или несколькими файловыми (сокетными) дескрипторами (см. ниже пример в разделе 4.5.1.1) и который будет управлять перекрывающимися I/O операциями, используя определенное количество потоков для обслуживания завершенных запросов.

Для начала нам необходимо создать программный объект - порт завершения I/O, который будет использоваться, чтобы управлять множественными I/O-запросами для любого количества сокетных дескрипторов. Это выполняется вызовом функции CreateIoCompletionPort(), которая определена как:

HANDLE CreateIoCompletionPort(

HANDLE FileHandle,

HANDLE ExistingCompletionPort,

DWORD CompletionKey,

DWORD NumberOfConcurrentThreads

);

Прежде чем рассматривать параметры подробно, следует отметить, что эта функция фактически используется для двух различных целей:

Чтобы создать объект порта завершения

Связать дескриптор с портом завершения

Когда Вы первоначально создаете объект порта завершения, интерес представляет единственный параметр - NumberOfConcurrentThreads; первые три параметра не существенны. Параметр NumberOfConcurrentThreads специфичен, потому что он определяет число потоков, которым позволяется выполниться одновременно на порте завершения. По идее, нам нужен только один поток для каждого отдельного процессора, чтобы обслужить порт завершения и избежать переключения контекста потока. Значение для этого параметра равное 0 сообщает системе разрешить иметь столько потоков, сколько процессоров имеется в системе. Следующий вызов создает порт завершения I/O:

CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL, 0, 0);

В результате функция возвратит дескриптор, который используется, чтобы идентифицировать порт завершения, когда на него будет назначен дескриптор сокета.

Модели ввода / вывода

Select (Выбор)

Эта модель обеспечивает более контролируемый способ блокирования. Хотя она позволяет работать и с блокирующими socket’ами, я остановлюсь на неблокирующем режиме. Принцип этой модели станет понятным, если взглянуть на иллюстрацию:

Диалог программы и WinSock будет следующим:

Программа: «Отправь-ка эти данные»

WinSock: «Я не могу сделать это сейчас»

Программа: «Хорошо, скажешь мне, когда будет наилучший момент, чтоб повторить попытку»

WinSock: «Конечно, повиси минутку»

«Пробуй снова!»

Программа: «Отправь-ка эти данные»

WinSock: «Сделано!»

Вы, возможно, заметили, что эта модель выглядит подобно блокирующему socket’у. Это, потому что select действительно блокирует. Первый вызов пытается выполнить WinSock операцию. В этом случае операция заблокировала бы выполнение основного процесса, но функция не может быть выполнена и она завершается неудачей. Тогда управление передается основному потоку программы, который, в свою очередь, вызывает select-метод (т.е. программа обращается к модели, что бы определить подходящее время для повторной попытки). Он будет ждать наилучшего момента, чтобы повторить WinSock функцию.

Но тут может возникнуть вполне справедливый вопрос: если данная модель блокирует, то почему мы используем ее для неблокирующих socket’ов? Дело в том, что этот способ может «ждать» при многократных событиях. Ниже приведен прототип функции select:

select (nfds:DWORD, readfds:DWORD, writefds:DWORD, exceptfds:DWORD, timeout:DWORD)

Select определяет статус одного или нескольких socket’ов, предоставляя синхронизацию ввода/вывода, если это необходимо. Первый параметр игнорируется, последний параметр используется для определения оптимального времени «ожидания» функции. Остальные параметры определяют набор socket’ов:

readfds – набор socket’ов, которые будут проверены на возможность чтения.

writefds - набор socket’ов, которые будут проверены на возможность записи.

exceptfds - набор socket’ов, которые будут проверены на наличие ошибок.

«Возможность чтения» значит, что данные прибыли на socket, и, что само чтение после select’а аналогично получению данных. «Возможность записи» значит, что сейчас подходящее время для передачи данных, т.к. получатель, возможно, готов принять их. Exceptfds используется, чтобы «словить» ошибки из неблокирующих соединений.

WSAASyncSelect

Большинство оконных программ используют специальные диалоговые окна, что бы получить информацию от пользователя или наоборот. WinSock обеспечивает способ взаимодействия уведомлений о сетевых событиях с обработкой сообщений Windows. Функция WSAAsyncSelect позволяет зарегистрировать уведомление для определенного сетевого события в виде привычного сообщения Windows.

WSAAsyncSelect (s:DWORD, hWnd:DWORD, wMsg:DWORD, lEvent:DWORD)

Эта функция требует специального сообщения (wMsg), которое выбирает пользователь. А оконная процедура должна обработать это самое сообщение. lEvent является битовой маской, которая определяет событие, о котором будет сообщено. Рисунок для данной модели можно сделать таким:

Допустим, что первое сообщение хочет отправить какие-то данные socket’у, используя send. Так как socket неблокирующий, функция будет завершена мгновенно. Вызов функции может завершиться успешно, но тут этого не происходит. Предполагая, что WSAAsyncSelect была настроена таким образом, что сообщит нам о событии FD_WRITE, в конечном итоге мы получим сообщение от WinSock, говорящее нам о том, что данное событие произошло. В данном случае это событие FD_WRITE, которое означает что-то типа «Я готово, попробуй переслать свои данные». Таким образом, в обработчике сообщения программа пытается переслать данные, и эта попытка завершается успехом.

Беседа между программой и WinSock подобна модели select, различие лишь в методе уведомления: оконное сообщение вместо синхронного вызова select’а. В то время как select блокирует основной процесс, ожидая пока произойдет событие, программа, использующая WSAAsyncSelect, может продолжить обработку сообщений Windows до тех пор, пока не происходит никаких событий:

Программа регистрируется для уведомления о сетевых событиях через оконные сообщения

Программа: «Отправь-ка эти данные»

WinSock: «Я не могу сделать это сейчас»

Программа обрабатывает некоторое сообщение

Программа обрабатывает другое сообщение

Программа получает уведомляющее сообщение от WinSock

Программа: «Отправь-ка эти данные»

WinSock: «Сделано! »

WSAAsyncSelect обеспечивает более «Windows’овский» способ уведомления и он довольно прост в использовании. Для серверов с низкой пропускной способностью (меньше 1000 соединений) этот способ вполне хорош. Недостатком является то, что оконные сообщения, сами по себе, не очень быстрые, а так же в том, что для использования этой модели требуются окна (т.е. программа должна быть GUI).

WSAEventSelect

Примечание: под «объектом события» далее будет пониматься какое-то определенное сетевое событие. Дело в том, что тут событие рассматривается, как класс =).

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

Программа регистрируется для уведомления о сетевых событиях через объекты событий

Программа: «Отправь-ка эти данные»

WinSock: «Я не могу сделать это сейчас»

Программа ждет события, чтобы сигнализировать о нем

Программа: «Отправь-ка эти данные»

WinSock: «Сделано! »

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

Поначалу эта модель похожа на блокирующую: Вы ждете событие, о котором Вам будет сообщено. Это верно, но в тоже самое время Вы можете создать свой объект события. Все объекты события являются частью WinAPI, которую использует WinSock. В WinSock есть некоторые функции для создания объектов, но фактически это API функции в WinSock упаковке.

Все, что WinSock делает в этой модели, это сигнализирует объект события, когда это событие должно произойти.

Функция, с помощью которой регистрируется сетевое событие WSAEventSelect:

WSAEventSelect (s:DWORD, hEventObject:DWORD, lNetworkEvents:DWORD)

WSAAsyncSelect отправит Вам сообщение о произошедшем сетевом событии (FD_READ, FD_WRITE, и т.д.) В отличие от WSAAsyncSelect, у WSAEventSelect есть только один способ уведомления: сигнализирование объекта событий. Это позволяет использовать данную модель как в GUI приложениях, так и в консольных. Какие события произошли можно узнать с помощью WSAEnumNetworkEvents.

Соседние файлы в папке Сетевое программирование от Ивана Ерохина