В принципе, события FD_XXX
разных сокетов можно привязать к одному сокетному событию, но этой возможностью обычно не пользуются, т. к. в WinSock2 отсутствуют средства, позволяющие определить, событие на каком из сокетов привело к взведению сокетного события. Поэтому приходится для каждого сокета создавать отдельное событие.
Как и в случае с WSAAsyncSelect
при вызове WSAEventSelect
сокет переводится в неблокирующий режим. Повторный вызов WSAEventSelect
для данного сокета отменяет результаты предыдущего вызова (т. е. невозможно связать разные события FD_XXX
одного сокета с разными сокетными событиями). Сокет, созданный в результате вызова accept или WSAAccept
наследует связь с сокетными событиями, установленную для слушающего сокета.
Существует весьма важное различие между использованием оконных сообщений и сокетных событий для оповещения о том, что происходит на сокете.
Предположим, с помощью функции WSAAsyncSelect
события FD_READ
, FD_WRITE
и FD_CONNECT
связаны с некоторым оконным сообщением. Пусть происходит событие FD_CONNECT
. В очередь окна помещается соответствующее сообщение. Затем, до того, как предыдущее сообщение будет обработано, происходит FD_WRITE
. В очередь окна помещается еще одно сообщение, которое информирует об этом. И наконец, при возникновении FD_READ
в очередь будет помещено третье сообщение. Затем оконная процедура получит их по очереди и обработает.
Теперь рассмотрим ситуацию, когда те же события связаны с сокетным событием. Когда происходит FD_CONNECT
, сокетное событие взводится. Теперь если FD_WRITE
и FD_READ
произойдут до того, как сокетное событие будет сброшено, оно уже не изменит своего состояния. Таким образом, программа, работающая с асинхронными сокетами, основанными на событиях, должна, во-первых, учитывать, что взведенное событие может означать несколько событий FD_XXX
, а во-вторых, иметь возможность узнать, какие именно события произошли с момента последней проверки. Для получения этой информации предусмотрена функция WSAEnumNetworkEvents
, прототип которой приведен в листинге 2.60.
WSAEnumNetworkEvents
// ***** Описание на C++ *****
int WSAEnumNetworkEvents(SOCKET s, WSAEVENT hEventObject, LPWSANETWORKEVENTS lpNetworkEvents);
// ***** Описание на Delphi *****
function WSAEnumNetworkEvents(S: TSocket; hEventObject: TWSAEvent; var NetworkEvents: TWSANetworkEvents): Integer;
Функция WSAEnumNetworkEvents
через параметр NetworkEvents
возвращает информацию о том, какие события произошли на сокете S с момента последнего вызова этой функции для данного сокета (или с момента запуска программы, если функция вызывается в первый раз). Параметр hEventObject
необязательный, он определяет сокетное событие, которое нужно сбросить. Использование этого параметра позволяет обойтись без явного вызова функции WSAResetEvent
для сброса события. Как и большинство функций WinSock, функция WSAEnumNetworkEvents
возвращает ноль в случае успеха и ненулевое значение при возникновении ошибки.
Запись TWSANetworkEvents
содержит информацию о произошедших событиях об ошибках (листинг 2.61).
TWSANetworkEvents
// ***** Описание на C++ *****
typedef struct _WSANETWORKEVENTS {
long lNetworkEvents;
int iErrorCode[FD_MAX_EVENTS];
} WSANETWORKEVENTS, *LPWSANETWORKEVENTS;
// ***** Описание на Delphi *****
TWSANetworkEvents = packed record
lNetworkEvents: LongInt;
iErrorCode: array[0..FD_MAX_EVENTS — 1] of Integer;
end;
Константа FD_MAX_EVENTS
определяет количество разных типов событий и в данной реализации равна 10.
Значения констант FD_XXX
представляют собой степени двойки, поэтому их можно объединять операцией арифметического ИЛИ без потери информации. Поле lNetworkEvents
является таким объединением всех констант, задающих события, которые происходили на сокете. Другими словами, если результат операции (lNetworkEvents and FD_XXX
) не равен нулю, значит, событие FD_XXX
происходило на сокете.