Если произойдет ошибка во время вызова метода Connect, то она будет очищена самостоятельно перед возбуждения соответствующего исключения. Поэтому, try здесь после вызова метода Connect на не перед. Тем не менее, если исключение случится во время передачи данных, то будет возбуждено исключение raised. Сокет останется подсоединенным. Это позволяет вам повторить операцию передаче или отсоединиться. В приведенном выше примере, не делается никакой дополнительной обработки и сокет отсоединяется по любой ошибке, и производится нормальное завершение.
Для обработки ошибок во время соединения и отделения от других ошибок связи, требуется изменить ваш код:
try
IdTCPClient1.Connect;
try
try
// Do your communications here
finally
IdTCPClient1.Disconnect;
end;
except
on E: EIdException do
begin
ShowMessage('An network error occurred during communication: ' + E.Message);
end;
on E: Exception do
begin
ShowMessage('An unknown error occurred during communication: ' + E.Message);
end;
end;
except
on E: EIdException do
begin
ShowMessage('An network error occurred while trying to connect: ' + E.Message);
end;
on E: Exception do
begin
ShowMessage('An unknown error occurred while trying to connect: ' + E.Message);
end;
end;
Данный код не только проверяет исключения, которые возникают во время соединения, но и отделяет эти ошибки от других ошибок связи. Дальше исключения Indy изолируются от других исключений.
6.3. Исключения это не ошибки
Многие разработчики серьезно считаю, что исключения это ошибки. Но это не так. Если бы это было так, то Borland бы назвал из ошибками, а не исключениями.
Исключение – это что-то, что за пределами ординарного. В терминах программирования, исключение – это что-то, что прерывает нормальный поток исполнения.
Исключения используются для представления ошибок в Delphi и по этому большинство исключений это ошибки. Тем не менее, есть такие исключения, как EAbort, которое не является ошибкой. Indy также определяет ряд исключений, которые не являются ошибками. Такие исключения, как правило, наследованы от EIdSilentException и могут быть легко отделены от ошибок и других исключений. Более сложный пример можно посмотреть в EIdConnClosedGracefully.
6.4. Компонент TIdAntiFreeze
Indy имеет специальный компонент, который прозрачно разрешает проблему с замораживанием пользовательского интерфейса. Достаточно одного экземпляра компонента TIdAntiFreeze в приложении, позволяя использовать блокирующие вызовы в главном кодовом потоке, без замораживания пользовательского интерфейса.
TIdAntiFreeze работает внутренне, независимо от вызова стека, и позволяет обрабатывать сообщения в течении периода таймаута. Внешний вызовы Indy продолжают быть блокированы и их код работает точно так же, как и без компонента TIdAntiFreeze.
Поскольку пользовательский интерфейс замораживается только при вызове блокирующих сокетов в главном кодовом потоке, TIdAntiFreeze влияет только на вызовы Indy, сделанные из главного кодового потока. Если приложение использует вызовы Indy из других потоков, TIdAntiFreeze не требуется. Но если используется, то влияет на вызовы сделанные только из главного кодового потока.
Использование TIdAntiFreeze немного замедляет работу сокетов. Сколько давать приоритета приложению задается в свойствах TIdAntiFreeze. Причина, по которой TIdAntiFreeze замедляет сокетовые операции, состоит в том, что главному кодовому потоку разрешается обрабатывать сообщения. По этому надо позаботиться, чтобы не позволять много времени отводилось обработке сообщений. Это включает большинство таких событий, как OnClick, OnPaint, OnResize и многие другие. Поскольку неблокирующие сокеты тоже обмениваются сообщениями, этаже проблема относится и к ним. С Indy и с помощью использования TIdAntiFreeze, программист получает полный контроль.
6.5. Пример - Проверка почтового индекса - клиент
Данный пример – это клиент, протокол просмотра почтовых индексов. Протокол очень простой и предполагается, что сервер уже реализован. В данной главе рассматривается только клиент.
Клиент обеспечивает получении имени города и штата по почтовому индексу (Zip код для американских пользователей). Исходные данные находятся на сервере для американских почтовых индексов. Американские почтовые индексы (называемые zip коды) состоят из 5 цифр.
Код сервера будет приведен позже.
6.5.1. Проверка почтового индекса - протокол
Протокол клиента очень прост, он содержит только две команды:
· | Lookup <почтовый код 1> < почтовый код 2> ... |
Общение с сервером выглядит так:
Server: 204 Post Code Server Ready.
Client: lookup 16412
Server: 200 Ok
Server: 16412: EDINBORO, PA
Server: .
Client: lookup 37642 77056
Server: 200 Ok
Server: 37642: CHURCH HILL, TN
Server: 77056: HOUSTON, TX
Server: .
Client: quit
Server: 201-Paka!
Server: 201 4 requests processed.
The server responds with a greeting when the client connects. Greetings and replies to commands typically contain a 3 digit number specifying status. This will be covered more in detail in later sections.
После приветственного сообщения сервер готов принимать запросы от клиента. Если принята команда Lookup – сервер отвечает списком почтовых кодов и далее соответствующим именем города и штата. Ответ заканчивается строкой с единственным символом <точка>. Клиент может посылать множество команд, пока не выдаст команду Quit, после происходит рассоединение.
6.5.2. Объяснение кода
Клиент просмотра почтового кода содержит две кнопки, listbox и memo. Одна кнопка используется для очистки окна результатов, а другая для получения ответов от сервера и запрос информации от него. Результаты помещаются в listbox.
В обычном приложении, пользователь должен предоставить информацию об узле, порте и возможно о прокси. Но для демонстрации данная информация указана в коде. В качестве узла используется 127.0.0.1 и порт 6000.
Когда пользователь нажимает на кнопку Lookup, то выполняется следующий обработчик:
procedure TformMain.butnLookupClick(Sender: TObject);
var
i: integer;
begin
butnLookup.Enabled := true;
try
lboxResults.Clear;
with Client do
begin
Connect;
try
// Read the welcome message
GetResponse(204);
lboxResults.Items.AddStrings(LastCmdResult.Text);
lboxResults.Items.Add('');
// Submit each zip code and read the result
for i := 0 to memoInput.Lines.Count - 1 do
begin
SendCmd('Lookup ' + memoInput.Lines[i], 200);
Capture(lboxResults.Items);
lboxResults.Items.Add('');
end;
SendCmd('Quit', 201);
finally
Disconnect;
end;
end;
finally
butnLookup.Enabled := True;
end;
end;
Методы Indy, использованные здесь, объясняются только коротко, поскольку подробно они рассмотрены в других главах.
Когда код выполняется, то блокируется кнопка, чтобы пользователь не мог послать другой запрос, пока не закончен текущий. Вы можете подумать, что это не возможно, поскольку событие нажатия кнопки обрабатывается с помощью сообщений. Но поскольку данный пример использует TIdAntiFreeze, который вызывает Application.ProcessMessages и позволяет обрабатывать события отрисовки, так и другие события. По этой причине вы должны побеспокоиться о защите пользовательского интерфейса.
Используя TIdTCPClient (Client) – бросьте его на форму во время проектирования и попробуйте подключиться к серверу и подождите приветствия от сервера. GetResponse читает ответы и возвращает ответы как результат. В данном случае результат отбрасывается, но GetResult знает, что надо проверить ответ на число 204. Если Сервет отвечает другим кодом, то возбуждается исключение. Сервер может отвечать разными кодами, если он, например, очень, находится на профилактике и так далее.
Для каждого почтового индекса, который вводит пользователь, пример посылает команду lookup на сервер и ожидает код ответа 200. Если SendCmd закончится успешно, пример вызывает функцию Capture, которая читает ответы, пока не поступит с единственной точкой в строке. Поскольку демонстрационный пример за раз посылает одну команду, то ожидается одна строка в ответ, или ее отсутствие если индекс неверный.
По окончанию пример шлет команду Quit и ожидает ответа с кодом 201, который означает, что сервер понял и отсоединяет клиента. Правильным поведением, является всегда посылка команды Quit, чтобы обе стороны знали что произошло разъединение