А. Григорьев - О чём не пишут в книгах по Delphi
- Название:О чём не пишут в книгах по Delphi
- Автор:
- Жанр:
- Издательство:БХВ-Петербург
- Год:2008
- Город:СПб
- ISBN:978-5-9775-019003
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
А. Григорьев - О чём не пишут в книгах по Delphi краткое содержание
Рассмотрены малоосвещённые вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные механизмы их работы, особенности для протоколов TCP и UDP и др. Большое внимание уделено разбору ситуаций возникновения ошибок и получения неверных результатов в "простом и правильном" коде. Отдельно рассмотрены особенности работы с целыми, вещественными и строковыми типами данных, а также приведены примеры неверных результатов, связанных с ошибками компилятора, VCL и др. Для каждой из таких ситуаций предложены методы решения проблемы. Подробно рассмотрен синтаксический анализ в Delphi на примере арифметических выражений. Многочисленные примеры составлены с учётом различных версий: от Delphi 3 до Delphi 2007. Прилагаемый компакт-диск содержит примеры из книги.
Для программистов
О чём не пишут в книгах по Delphi - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Между функциями connect
и WSAConnect
существует небольшое различие при работе с сокетами, не поддерживающими соединение. Как вы знаете из разд. 2.1.9 , функция connect
может использоваться с такими сокетами для задания адреса отправки по умолчанию и автоматической фильтрации входящих пакетов. Для того чтобы отменить такое "соединение", нужно при вызове функции connect
указать адрес INADDR_ANY
и нулевой порт. В случае WSAConnect
для отмены "соединения" требуется, чтобы все без исключения поля структуры Name
, включая sin_family
, были нулевыми. Это сделано для того, чтобы обеспечить независимость от протокола: при любом протоколе для разрыва "соединения" должно устанавливаться одно и то же значение Name
.
Если программа не предъявляет требований к качеству обслуживания, то для протоколов TCP и UDP функция WSAConnect
не предоставляет никаких преимуществ по сравнению с connect
.
Функция accept
из стандартной библиотеки сокетов позволяет серверу извлечь из очереди соединений информацию о подключившемся клиенте и создать сокет для его обслуживания. Эти действия выполняются безусловно, для любых подключившихся клиентов. Если сервер допускает подключение не любых клиентов, а только тех, которые отвечают некоторым условиям (для протокола TCP эти условия могут заключаться в том, какие IP-адреса и какие порты допустимо использовать клиентам), сразу после установления соединения его приходится разрывать, если клиент не удовлетворяет этим условиям. Для упрощения этой операции в WinSock 2 предусмотрена функция WSAAccept
, прототип которой приведен в листинге 2.40.
WSAAccept
// ***** Описание на C++ *****
SOCKET WSAAccept(SOCKET S, struct sockaddr FAR* addr, LPINT addrlen, LPCONDITIONPROC lpfnCondition, dwCallbackData);
// ***** описание на Delphi *****
function WSAAccept( S: TSocket; Addr: PSockAddr; AddrLen: PInteger; lpfnCondition: TConditionProc; dwCallbackData: DWORD): TSocket;
По сравнению с уже известной нам функцией accept
функция WSAAccept
имеет два новых параметра: lpfnCondition
и dwCallbackData
. lpfnCondition
является указателем на функцию обратного вызова. Эта функция объявляется и реализуется программой. WSAAccept
вызывает ее внутри себя и в зависимости от ее результата принимает или отклоняет соединение. Параметр dwCallbackData
не имеет смысла для самой функции WSAAccept
и передается без изменений в функцию обратного вызова. Тип TConditionProc
должен быть объявлен следующим образом (листинг 2.41).
TConditionProc
// ***** Описание на C++ *****
typedef (int*)(LPWSABUF lpCallerId, LPWSABUF lpCallerData, LPQOS lpSQOS, LPQOS lpGQOS, LPWSABUF lpCalleeId, LPWSABUF lpCalleeData, GROUP FAR* g, DWORD dwCallbackData) LPCONDITIONPROC;
// ***** Описание на Delphi *****
TConditionProc = function(lpCallerId, lpCallerData: PWSABuf; lpSQOS, lpGQOS: PQOS; lpCalleeID, lpCalleeData: PWSABuf; g: PGroup; dwCallbackData: DWORD): Integer; stdcall;
Параметр lpCallerId
указывает на буфер, в котором хранится адрес подключившегося клиента. При работе со стеком TCP/IP lpCallerId^.Len
будет равен SizeOf(TSockAddr)
, a lpCallerId^.Buf
будет указывать на структуру TSockAddr
, содержащую адрес клиента. Параметр lpCallerData
определяет буфер, в котором хранятся данные, переданные клиентом при соединении. Как уже отмечалось, протоколы стека TCP/IP не поддерживают передачу данных при соединении, поэтому для них этот параметр будет равен nil. Параметры lpSQOS
и lpGQOS
задают требуемое клиентом качество обслуживания для сокета и для группы соответственно. Так как группы сокетов в текущей реализации WinSock не поддерживаются, параметр lpGQOS
будет равен nil
. Параметр lpSQOS
тоже будет равен nil
, если клиент не задал качество обслуживания при соединении.
Параметр lpCalleeId
содержит адрес интерфейса, принявшего соединение (поля структуры при этом используются так же, как у параметра lpCallerId
). Ранее уже обсуждалось, что сокет, привязанный к адресу INADDR_ANY
, прослушивает все сетевые интерфейсы, имеющиеся на компьютере, но каждое подключение, созданное с его помощью, использует конкретный интерфейс. Параметр lpCalleeId
содержит адрес, привязанный к конкретному соединению. Параметр lpCalleeData
указывает на буфер, в который сервер может поместить данные для отправки клиенту. Этот параметр также не имеет смысла для протокола TCP, не поддерживающего отправку данных при соединении.
Параметр g
выходной, он позволяет управлять присоединением создаваемого функцией WSAAccept
сокета к группе. Параметр, как и все, связанное с группами, зарезервирован для использования в будущем.
Если вы пользуетесь старой версией MSDN, то можете не обнаружить там описания параметра g
— оно там отсутствует. Видимо, просто по ошибке.
И наконец, через параметр dwCallbackData
в функцию обратного вызова передается значение параметра dwCallbackData
, переданное в функцию WSAAccept
. Программист должен сам решить, как ему интерпретировать это значение.
Функция должна вернуть CF_ACCEPT
(0), если соединение принимается, CF_REJECT
(1), если оно отклоняется, и CF_DEFER
(2), если решение о разрешении или запрете соединения откладывается. Если функция обратного вызова вернула CF_REJECT
, to WSAAccept
завершается с ошибкой WSAECONNREFUSED
, если CF_DEFER
— то с ошибкой WSATRY_AGAIN
(в последнем случае соединение остаётся в очереди, и информация о нем вновь будет передана в функцию обратного вызова при следующем вызове WSAAccept
). Обе эти ошибки не фатальные, сокет остается в режиме ожидания соединения и может принимать подключения от новых клиентов.
Ранее уже обсуждалось, что функция connect
на стороне клиента считается успешно завершенной тогда, когда соединение встало в очередь, а не тогда, когда оно реально принято сервером через функцию accept
. По умолчанию для клиента, соединение с которым сервер отклонил, нет разницы, вызвал ли сервер функцию WSAAccept
и сразу отклонил соединение, или установил его с помощью accept
, а потом разорвал. В обоих случаях клиент сначала получит информацию об успешном соединении с сервером, а потом это соединение будет разорвано. Но при использовании WSAAccept
можно установить такой режим работы, когда сначала выполняется функция. заданная параметром lpCondition
, и лишь потом клиенту отправляется разрешение или запрет на подключение. Включается этот режим установкой параметра слушающего сокета SO_CONDITIONAL_ACCEPT
, что иллюстрирует листинг 2.42.
var
Cond: BOOL;
begin
Cond := True;
setsockopt(S, SOL_SOCKET, SO_CONDITIONAL_ACCEPT, PChar(@Cond), SizeOf(Cond));
Этот режим снижает нагрузку на сеть и повышает устойчивость сервера против DoS-атак, заключающихся в многократном подключении-отключении посторонних клиентов, поэтому в серьезных серверах рекомендуется использовать эту возможность.
Читать дальшеИнтервал:
Закладка: