А. Григорьев - О чём не пишут в книгах по Delphi
- Название:О чём не пишут в книгах по Delphi
- Автор:
- Жанр:
- Издательство:БХВ-Петербург
- Год:2008
- Город:СПб
- ISBN:978-5-9775-019003
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
А. Григорьев - О чём не пишут в книгах по Delphi краткое содержание
Рассмотрены малоосвещённые вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные механизмы их работы, особенности для протоколов TCP и UDP и др. Большое внимание уделено разбору ситуаций возникновения ошибок и получения неверных результатов в "простом и правильном" коде. Отдельно рассмотрены особенности работы с целыми, вещественными и строковыми типами данных, а также приведены примеры неверных результатов, связанных с ошибками компилятора, VCL и др. Для каждой из таких ситуаций предложены методы решения проблемы. Подробно рассмотрен синтаксический анализ в Delphi на примере арифметических выражений. Многочисленные примеры составлены с учётом различных версий: от Delphi 3 до Delphi 2007. Прилагаемый компакт-диск содержит примеры из книги.
Для программистов
О чём не пишут в книгах по Delphi - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Событие, связанное с сокетом функцией 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.
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
происходило на сокете.
Массив iErrorCode
содержит информацию об ошибках, которыми сопровождались события FD_XXX
. Для каждого события FD_XXX
определена соответствующая константа FD_XXX_BIT
(т.е. константы FD_READ_BIT
, FD_WRITE_BIT
и т.д.). Элемент массива с индексом FD_XXX_BIT
содержит информацию об ошибке, связанной с событием FD_XXX
. Если операция прошла успешно, этот элемент содержит ноль, в противном случае — код ошибки, которую в аналогичной ситуации вернула бы функция WSAGetLastError
после выполнения соответствующей операции на синхронном сокете.
Таким образом, программа, использующая асинхронный режим, основанный на событиях, должна выполнить следующие действия. Во-первых, создать сокет и установить соединение. Во-вторых, привязать события FD_XXX
к сокетному событию. В-третьих, организовать цикл, начинающийся с вызова WSAWaitForMultipleEvents
, в котором с помощью WSAEnumNetworkEvents
определять, какое событие произошло, и обрабатывать его. При возникновении ошибки на сокете цикл должен завершаться.
Сокетные события могут взводиться не только в результате событий на сокете, но и вручную, с помощью функции WSASetEvent
. Это дает нити, вызвавшей функцию WSAWaitForMultipleEvents
, возможность выходить из состояния ожидания не только при возникновении событий на сокете, но и по сигналам от других нитей. Типичная область применения этой возможности — для тех случаев, когда программа может как отвечать на запросы от удаленного партнера, так и отправлять ему что-то по собственной инициативе. В этом случае могут использоваться два сокетных события: одно связывается с событием FD_READ
для оповещения о поступлении данных, а второе не связывается ни с одним из событий FD_XXX
, а устанавливается другой нитью тогда, когда необходимо отправить сообщение. Нить, работающая с сокетом, ожидает взведения одного из этих событий и в зависимости от того, какое из них взведено, читает или отправляет данные.
Интервал:
Закладка: