Дескрипторы событий, взведения которых ожидает нить, должны храниться в массиве, размер которого передаётся через параметр cEvents
, а указатель — через параметр lphEvents
. Параметр fWaitAll
определяет, что является условием окончания ожидания: если он равен True
, ожидание завершается, когда все события из переданного массива оказываются во взведенном состоянии, если False
— когда оказывается взведенным хотя бы одно из них. Параметр dwTimeout
определяет тайм-аут ожидания в миллисекундах. В WinSock 2 определена константа WSA_INFINITE
(совпадающая по значению со стандартно константой INFINITE
), которая задает бесконечное ожидание. Параметр fAlertable
нужен при перекрытом вводе-выводе: мы рассмотрим его позже в fAlertable
должен быть равен False
.
Существует ограничение на число событий, которое можно ожидать с помощью данной функции. Максимальное число событий определяется константой WSA_MAXIMUM_WAIT_EVENTS
, которая в данной реализации равна 64.
Результат, возвращаемый функцией, позволяет определить, по каким причинам закончилось ожидание. Если ожидалось взведение всех событий (fWaitAll = True
), и оно произошло, функция возвращает WSA_WAIT_EVENT_0
(0). Если ожидалось взведение хотя бы одного из событий, возвращается WSA_WAIT_EVENT_0 + Index
, где Index
— индекс взведенного события в массиве lphEvents
(отсчет индексов начинается с нуля). Если ожидание завершилось по тайм-ауту, возвращается значение WSA_WAIT_TIMEOUT
(258). И наконец, если произошла какая-либо ошибка, функция возвращает WSA_WAIT_FAILED
($FFFFFFFF
).
Существует еще одно значение, которое может возвратить функция WSAWaitForMultipleEvents
: WAIT_IO_COMPLETION
(это константа из стандартной части Windows API, она объявлена в модуле Windows
). Смысл этого результата и условия, при которых он может быть возвращен, мы рассмотрим в
Функции, которые мы рассматривали до сих пор, являются аналогами системных функций для стандартных событий. Теперь мы переходим к рассмотрению тех функций, которые отличают сокетные события от стандартных. Главная из них — WSAEventSelect
, позволяющая привязать события, создаваемые с помощью WSACreateEvent
, к тем событиям, которые происходят на сокете. Прототип этой функции приведен в листинге 2.59.
WSAEventSelect
// ***** Описание на C++ *****
int WSAEventSelect(SOCKET s, WSAEVENT hEventObject, long lNetworkEvents);
// ***** описание на Delphi *****
function WSAEventSelect(S: TSocket; hEventObject: TWSAEvent; lNetworkEvents: LongInt): Integer;
Эта функция очень похожа на функцию WSAAsyncSelect
, за исключением того, что события FD_XXX
привязываются не к оконным сообщениям, а к сокетным событиям. Параметр S
определяет сокет, события которого отслеживаются, параметр hEventObject
— событие, которое должно взводиться при наступлении отслеживаемых событий, lNetworkEvents
— комбинация констант FD_XXX
, определяющая, с какими событиями на сокете связывается событие hSocketEvent
.
Функция WSAEventSelect
возвращает ноль, если операция прошла успешно, и SOCKET_ERROR
при возникновении ошибки.
Событие, связанное с сокетом функцией WSAEventSelect
, взводится при тех же условиях, при которых в очередь окна помещается сообщение при использовании WSAAsyncSelect
. Так, например, функция recv
взводит событие, если после ее вызова в буфере сокета еще остаются данные. Но, с другой стороны, функция recv
не сбрасывает событие, если данных в буфере сокета нет. А поскольку сокетные события не сбрасываются автоматически функцией WSAWaitForMultipleEvents
, программа всегда должна сбрасывать события сама. Так, при обработке FD_READ
наиболее типична ситуация, когда сначала сбрасывается событие, а потом вызывается функция recv
, которая при необходимости снова взводит событие. Здесь мы снова имеем проблему ложных срабатываний в тех случаях, когда данные извлекаются из буфера по частям с помощью нескольких вызовов recv
, но в данном случае проблему решить легче: не нужно отменять регистрацию событий, достаточно просто сбросить событие непосредственно перед последним вызовом recv
.