А. Григорьев - О чём не пишут в книгах по 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, а устанавливается другой нитью тогда, когда необходимо отправить сообщение. Нить, работающая с сокетом, ожидает взведения одного из этих событий и в зависимости от того, какое из них взведено, читает или отправляет данные.
Интервал:
Закладка: