Листинг 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
.
В принципе, события 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.
Листинг 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
возвращает ноль в случае успеха и ненулевое значение при возникновении ошибки.
Читать дальше
Конец ознакомительного отрывка
Купить книгу