Программирование в сетях Windows
.pdfiипрограммированияwinsock
long INetworkEvents
);
Здесь параметр 5 — интересующий нас сокет, а параметр hEventObject — объект «событие», полученный из вызова WSACreateEvent, который нужно связать с сокетом. Последний параметр INetworkEvents — битовая маска получаемая комбинацией масок типов сетевых событий, которые надо отслеживать. Подробно эти типы обсуждались при описании предыдущей модели — WSAAsyncSelect.
Усобытия, используемого в модели WSAEventSelect, два рабочих состояния
—свободное (signaled) и занятое (nonsignaled), а также два оперативных режима —ручного (manual) и автоматического сброса (auto reset). Первоначально событие создается в занятом состоянии и режиме ручного сброса. Когда на сокете происходит сетевое событие, связанный с эти событием объект становится занятым. Так как объект события создается в режиме ручного сброса, приложение ответственно за его возврат в занятое состояние после обработки ввода-вывода. Это можно сделать, вызвав функцию WSAResetEvenP.
BOOL WSAResetEvent(WSAEVENT hEvent);
Она принимает описатель события в качестве единственного параметра и возвращает TRUE или FALSE, в зависимости от успешности вызова. Закончив работу с объектом события, приложение должно вызвать функцию WSACloseEvent для освобождения системных ресурсов, используемых объектом:
BOOL WSACloseEvent(WSAEVENT hEvent);
Эта функция также принимает описатель события в качестве единственного параметра и возвращает TRUE или FALSE, в зависимости от успешности вызова.
Сопоставив сокет описателю события, приложение может начать обработку ввода-вывода, ожидая изменения состояния описателя. Функция WSAWaitForMultipleEvents будет ожидать сетевое событие и вернет управление, когда один из заданных описателей объектов событий перейдет в свободное состояние или когда истечет заданный таймаут.
DWORD WSAWaitForMultipleEvents( DWORD cEvents,
const WSAEVENT FAR * lphEvents, BOOL fWaitAll,
DWORD dwTimeout, BOOL fAlertable
);
Здесь параметры cEvents и lphEvents определяют массив объектов типа WSAEVENT, в котором cEvents — количество элементов, a lphEvents — указатель на массив. Функция WSAWaitForMultipleEvents поддерживает не более WSA_MAXIMUM_WAIT_EVENTS(64) объектовсобытий.Поэтомуданнаямодель ввода-вывода способна одновременно обслуживать максимум 64 сокета для каждого потока, вызывающего WSAWaitForMultipleEvents.
интерфейс прикладного программирования Wmsock
long INetworkEvents;
int iErrorCode[FD_MAX_EVENTS];
} WSANETWORKEVENTS, FAR * LPWSANETWORKEVENTS;
Последний параметр — ipNetworkEvents, принимает указатель на структуру WSANETWORKEVENTS, в которой передается тип произошедшего события и код ошибки. Параметр INetworkEvents определяет тип произошедшего события.
ПРИМЕЧАНИЕ При освобождении события иногда генерируется несколько типов сетевых событий. Например, интенсивно используемый сервер может одновременно получить сообщения FDJREAD и FDJWRITE.
Параметр iErrorCode — массив кодов ошибок, связанных с событиями из массива INetworkEvents. Для каждого типа сетевого события существует индекс события, обозначаемый тем же именем с суффиксом BIT. Например, для типа события FDJREAD идентификатор индекса в массиве iErrorCode обозначается FDJREADJ3IT. Вот анализ кода ошибки для события FD READ:
II Обработка уведомления FD_READ
if (NetworkEvents.INetworkEvents & FD_READ)
{
if (NetworkEvents.iErrorCode[FD_READ_BIT] != 0)
{
printf("FD_READ failed with error Xd\n", NetworkEvents.iErrorCode[FD_READ_BIT]);
Послеобработкисобытий,описанныхвструктуреWSANETWORKEVENTS приложение может продолжить ожидание сетевых событий на доступных сокетах. В листинге 8-6 показано применение модели WSAEventSelect для программирования сервера и управления событиями. Выделены обязательные этапы, лежащие в основе программирования сервера, способного обслуживать несколько сокетов одновременно.
Листинг8-6.Примерсерверавмоделиввода-выводаWSAEventSelect
SOCKET Socket[WSA_MAXIMUM_WAIT_EVENTS];
WSAEVENT Event[WSA_MAXIMUM_WAIT_EVENTS]; SOCKET Accept, Listen;
DWORD EventTotal = 0; DWORD Index;
// Настройка ТСР-сокета для прослушивания порта 5150 Listen = socket (PF_INET, SOCK_STREAM, 0);
InternetAddr.sin_family = AF_INET;
InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
InternetAddr.sin_port = htons(5150);
bmd(Listen, (PSOCKADDR) MnternetAddr, |
|
Sizeof(InternetAddr)); |
ыГЧ* |
Г Л А ВА 8 Ввод-вывод в Wmsock |
223 |
Листинг 8-6. (продолжение) closesocket(Socket[Index - WSA_WAIT_EVENT_O]),
//Удаление сокета и связанного события
//из массивов сокетов (Socket) и событий (Event);
//уменьшение счетчика событий EventTotal CompressArrays(Event, Socket, &EventTotal);
Модель перекрытого ввода-вывода
Эта модель Winsock более эффективна, чем рассмотренные ранее. С ее помощью приложение может асинхронно выдать несколько запросов вводавывода, а затем обслужить принятые запросы после их завершения Модель доступна на всех платформах Windows, кроме Windows СЕ Схема ее работы основана на механизмах перекрытия ввода-вывода в Win32, доступных для выполнения операций ввода-вывода на устройствах с помощью функций
ReadFile и WrtteFtle.
Первоначально модель перекрытого ввода-вывода была доступна для приложений Winsock 1 1 только в Windows NT Приложения могли использовать эту модель, вызывая функции ReadFile и WnteFile для описателя сокета и указывая структуру перекрытия (описана далее) Начиная с версии Winsock 2, модель перекрытого ввода-вывода встроена в новые функции Winsock, такие как WSASend и WSARecv, и теперь доступна на всех платформах, где работает Winsock 2.
ПРИМЕЧАНИЕ Winsock 2 позволяет использовать перекрытый вводвывод с функциями ReadFile и WriteFile в Windows NT и 2000. Впрочем, эта функциональность не поддерживается для Windows 9x. Из соображений переносимости и производительности, применяйте функции
WSARecv и WSASend, а не ReadFile и WriteFile Мы рассмотрим лишь использование модели перекрытого ввода-вывода с новыми функциями Winsock 2
Чтобы задействовать модель перекрытого ввода-вывода для сокета, создайте сокет с флагом WSA_FLAG_OVERLAPPED:
s = WSASocket(AF_INET, SOCK.STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
Если вы создаете сокет функцией socket, а не WSASocket, флаг WSA_FLAG_ VERLAPPED задается неявно Создав сокет и привязав его к локальному интерфейсу, можно использовать перекрытый ввод-вывод, вызвав следующие Функции Winsock. WSASend, WSASendTo, WSARecv, WSARecvFrom, WSAIoctl, cceptEx, TransmitFile. Следует также указать необязательную структуру
WSAOVERLAPPED.
ак ВЬ1, возможно, уже знаете, каждая из этих функций связана с приемом, и П е редачей данных, или установлением соединения на сокете Их выпол-
224 |
ЧАСТЬ II Интерфейс прикладного программирования Wmsock |
нение потребует много времени, поэтому каждая функция может принимать структуру WSAOVERLAPPED в качестве параметра. Тогда они завершаются немедленно, даже если сокет работает в блокирующем режиме. В этом случае функция полагается на структуру WSAOVERLAPPED для завершения запроса ввода-вывода. Есть два способа завершения запросов перекрытого вво- да-вывода: приложение может ожидать уведомления от объекта «событие* (event object notification) или обрабатывать завершившиеся запросы процедурами завершения (completion routines). У всех перечисленных функций (кроме AcceptEx) есть еще один общий параметр — ipCompletionROUTINE. Это необязательный указатель на процедуру завершения, которая вызывается при завершении запроса перекрытого ввода-вывода. Сначала рассмотрим способ уведомления о событиях, а затем — использование процедур завершения.
Уведомление о событии
Для использования уведомления о событии в модели перекрытого ввода-вывода необходимо сопоставить объекты события со структурами WSAOVERLAPPED. Когда функции ввода-вывода, такие как WSASend и WSARecv, вызываются со структурой WSAOVERLAPPED в качестве параметра, они завершаются немедленно. В большинстве случаев эти вызовы возвращают ошибкуSOCKETr_ERROR При этомфункция WSAGetLastErrorвозвращаетзначение WSA_IO_PENDING. Этопросто означает, что операция ввода-вывода продолжается. Приложению требуется определить, когда завершится перекрытый ввод-вывод. Об этом сообщает событие, связанное со структурой WSAOVERLAPPED. Структура WSAOVERLAPPED осуществляет связь между началом запроса ввода-вывода и его завершением:
typedef struct WSAOVERLAPPED
DWORD Internal; DWORD InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
WSAEVENT hEvent;
} WSAOVERLAPPED, FAR * LPWSAOVERLAPPED;
Поля Internal, InternalHigh, Offset и OffsetHigh используются системой и не должны задаваться приложением. Поле hEvent позволяет приложению связать описатель объекта «событие» с сокетом. Этот описатель создают, вызвав функцию WSACreateEvent, как и в модели WSAEventSelect. После создания описателя события достаточно присвоить его значение полю hEvent структуры WSAOVERLAPPED, после чего можно вызывать функции Winsock, использующие структуры перекрытой модели, такие как WSASend или WSARecv.
Когда запрос перекрытого ввода-вывода завершится, приложение должно извлечь его результаты. При уведомлении посредством событий, по завершении запроса Winsock освобождает объект «событие», связанный со струК" турой WSAOVERLAPPED. Так как описатель этого объекта содержится в структуре WSAOVERLAPPED, завершение запроса перекрытого ввода-вывода легко определить, вызвав функцию WSAWaitForMultipleEvents, описанную в в посвященном модели WSAEventSelect.
I № I D и пиiерфеис прикладного программирования Winsock
4.Асинхронный запрос WSARecv на сокет со структурой WSAOVERLAPPED в качестве параметра.
ПРИМЕЧАНИЕ Как правило, эта функция возвращает ошибку SOCKETJERROR со статусом WSA_1O_PENDING.
5.Вызов функции WSAWaitForMultipleEvents с использованием массива событий и ожидание освобождения события, связанного с запросом перекрытого ввода-вывода.
6.По завершении WSAWaitForMultipleEvents — сброс события функцией WSAResetEvent с массивом событий и обработка результатов запроса.
7.Определение состояния запроса перекрытого ввода-вывода функцией
WSAGetOverlappedResult.
8.Отправка нового запроса перекрытого ввода-вывода WSARecv на сокет.
9.Повтор шагов 5-8.
Этот пример легко расширить для обработки нескольких сокетов: выделите в отдельный поток части кода, обрабатывающего перекрытый вводвывод и разрешите главному потоку приложения обслуживать дополнительные запросы соединения.
Листинг 8-7. Перекрытый ввод-вывод с использованием уведомлений о событиях
void main(void)
{
WSABUF DataBuf; DWORD EventTotal = 0;
_e WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS]; WSAOVERLAPPED AcceptOverlapped;
SOCKET ListenSocket, AcceptSocket;
//Шаг 1:
//Инициализация Winsock и начало прослушивания
//Шаг2:
//Прием входящего соединения
AcceptSocket = accept(ListenSocket, NULL, NULL);
//Шаг3:
г в | // Формирование структуры перекрытого ввода-вывода
EventArray[EventTotal] = WSACreateEventO;
ZeroMemory(&AcceptOverlapped,
sizeof(WSAOVERLAPPED));
AcceptOverlapped.hEvent = EventArray[EventTotal];
DataBuf.len = DATA_BUFSIZE;