Делфи - Уроки - Работа с Indy в Delphi

Содержание:
• Применение многопоточности
• Построение собственного сетевого протокола

Применение многопоточности
Для уменьшения времени отклика и создания дружественного интерфейса в клиентских приложениях может быть использована многопоточность. Приложение, к рассмотрению которого мы переходим, демонстрирует технику применения многопоточности для построения клиента.
Для начала строится модуль с классом-наследником TThread, обычно применяющимся в Delphi для построения многопоточных приложений: основная функциональность нашего приложения обеспечивается экземпляром класса TIdTCPClient:

unit Unit2;
interface
uses
Classes, Math, SysUtils, IdBaseComponent, IdComponent,
IdTCPConnection, IdTCPClient;
type
TTCPClientThread = class(TThread)
private
FPort: Integer;
FHostID: String;
FCustomerInfo: String;
{ Private declarations }
protected
IdTCPClient: TIdTCPClient;
procedure Execute; override;
procedure UpdateList;
public
destructor Destroy; override;
property HostID: String read FHostID write FHostID;
property Port: Integer read FPort write FPort;
end;

implementation
uses Unit1;

procedure TTCPClientThread.UpdateList;
begin
Form1.Memo1.Lines.Add(FCustomerInfo);
end;

procedure TTCPClientThread.Execute;
begin
try
IdTCPClient := TIdTCPClient.Create(nil);
IdTCPClient.Host := FHostID;
IdTCPClient.Port := FPort;
while True do
begin
with IdTCPClient do
begin
if Terminated then Exit;
try
Connect;
try
WriteLn(IntToStr(RandomRange(1001, 1015)));
if Terminated then Exit;
FCustomerInfo := ReadLn;
Synchronize(UpdateList);
finally
Disconnect;
end; // try finally
except
end;
if Terminated then Exit;
sleep(RandomRange(1,Random(100)));
end; //with
end; //while True
except
//Something went wrong. Terminate the thread
Exit;
end;
end;

destructor TTCPClientThread.Destroy;
begin
if IdTCPClient <> nil then
IdTCPClient.Free;
inherited;
end;

initialization
Randomize;
end.

Метод UpDataList применяется для
синхронизации с VCL-компонентами, содержащимися на главной форме приложения.
Для имитации случайных обращений клиентов к серверу используется встроенный
в Pascal генератор псевдослучайных чисел.
Для того чтобы использовать данный модуль, необходимо в модуле главной формы создать несколько экземпляров класса TTCPClientThread в виде, например, динамического массива:


var
ThreadArray: array of TTCPClientThread;
Далее следует ввести код для метода, отвечающего за обработку события, сигнализирующего о нажатии кнопки:
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
begin
if Button1.Caption = 'Stop' then
begin
for i := low(ThreadArray) to High(ThreadArray) do
ThreadArray[i].Terminate;
Button1.Caption := 'Start';
end
else
begin
Button1.Caption := 'Stop';
SetLength(ThreadArray, SpinEdit1.Value);
for i:= 0 to Pred(SpinEdit1.Value) do
begin
ThreadArray[i] := TTCPClientThread.Create(True);
with ThreadArray[i] do
begin
FreeOnTerminate := True;
HostID := Edit1.Text;
Port := StrToInt(Edit2.Text);
Resume;
sleep(200); //wait briefly before
//creating next thread
end; //with
end;// for
end; //else
end;


После нажатия кнопки в соответствии со значением, установленным в SpinEdit1, порождаются экземпляры клиентов, независимо обращающиеся к базе данных с запросами.
Перейдем к более сложным примерам клиентских приложений. Создадим клиентское приложение, при помощи которого можно отправлять электронную почту с помощью протокола SMTP. Приложение также будет использовать два дополнительных класса из компонентов Indy: TIdMessage и TIdThread.
Чтобы послать сообщение посредством TIdSMTP, для начала нужно создать это сообщение, используя класс TIdMessage со страницы Indy Misc в качестве предка. Этот класс поддерживает формат сообщения, принятый в Internet. Классы TIdSMTP, TIdPOP3 и TIDNNTP используют класс TIdMessage, чтобы отправлять и принимать сообщения.
Создание и заполнение экземпляра класса TIdMessage во время выполнения демонстрирует следующий код:


var
LMsg: TIdMessage;

LMsg := TIdMessage.Create(nil);
try
with LMsg do
begin
From.Address := 'Me@MyDomain.com';
Recipients.Add.Address := 'You@YourDomain.com';
Subject := 'Test Subject';
Body.Text := FSubject;
end;
finally
LMsg.Free;
end;

Экземпляр этого класса может также создаваться на этапе проектирования приложения — для этого
достаточно просто поместить соответствующий компонент на форму.
Для создания сообщения, которое затем будет отправлено по электронной почте, как минимум должны быть заполнены свойства From и Recipients класса TidMessage (адрес отправителя и получателя). Свойство From имеет тип TIdEmailAddressItem. Класс TidEmailAddressItem выполняет обработку адресов электронной почты. В элементарном случае пользователю достаточно просто присвоить свойству Address этого класса свой электронный адрес. Свойство Recipients имеет тип TIdEmailAddressList, который является коллекцией классов TIdEmailAddressItem, так как рассылка может производиться по многим адресам одновременно (в нашем случае — по одному адресу). Заполнение свойства Subject типа String является необязательным — оно задает тему сообщения. Свойство Body типа TStringList содержит само сообщение, которое при помощи своего свойства Text может быть разбито на строки.
Использование экземпляра класса TIdSMTP тоже очень просто. Как минимум достаточно присвоить свойству Host этого класса IP-адрес или имя SMTP-сервера, который мы собираемся использовать для отправки сообщений. Кроме того, обязательно используются методы этого класса: Connect — для установления соединения (как обычно, с блокировкой); Send с одним параметром типа TIdMessage — для фактической передачи сообщения; фигурируют также методы Disconnect и Free, назначение которых очевидно. Вышесказанное можно проиллюстрировать следующим кодом:


with TIdSMTP.Create(nil) do c
try
Host := FSMTPServer;
Connect;
try
Send(LMsg);
finally
Disconnect;
end; //try Synchronize
finally
Free;
end; // with TIdSMTP try


Как уже говорилось, чтобы оградить прорисовку визуальной части приложения от замедления при сбоях, которые могут происходить в каналах, можно использовать компонент TIdAntiFreeze. При этом весь клиентский код должен располагаться в одном потоке. Но чтобы полностью изолировать клиентский код от VCL-компонентов главной формы или разрешить одновременное выполнение нескольких клиентов, лучше иметь многопоточное приложение. Для этого компоненты Indy имеют специальный класс TIdThread (наследник класса TThread). Главная особенность этого класса заключается в том, что при использовании TIdThread не перекрывается метод Execute (как это требуется в Tthread), а перекрываетcя метод Run у всех потомков. Когда вторичные процессы становится активными, этот метод выполняется неоднократно — вплоть до исключения или указания о завершении процесса. Метод Run также поддерживает синхронизацию. Следующий код демонстрирует описание типа для такого потомка:


uses
IdThread;
type
TSMTPThread = class(TIdThread)
public
FFrom: string;
FMessage: string;
FRecipient: string;
FSMTPServer: string;
FSubject: string;
procedure Run; override;
end;


В этом примере метод Run просто использует значения, введенные в поля на главной форме, чтобы создать сообщение для электронной почты. Когда работа потока заканчивается, вызывается метод Stop, а процедура Run получает сообщение о завершении процесса. Вызов метода Stop фактически не закрывает вторичного процесса. Вместо этого появляется отметка о том, что завершение возможно в соответствии с алгоритмом метода Run. До тех пор пока нет выхода из метода Run, компоненты TIdThread не завершают вторичный процесс. При использовании TIdThread не вызывается метод Terminate, который наследуется от класса TThread. Всегда следует вызывать метод Stop, если нужно прекратить работу вторичного процесса. Ниже приведен код метода Run:


procedure TSMTPThread.Run;
var
LMsg: TIdMessage;
begin
LMsg := TIdMessage.Create(nil);
try
with LMsg do
begin
From.Address := FFrom;
Recipients.Add.Address := FRecipient;
Subject := FSubject;
Body.Text := FMessage;
end; //with LMsg
with TIdSMTP.Create(nil) do
try
Host := FSMTPServer;
Connect;
try
Synchronize(formMain.Connected);
Send(LMsg);
with TSyncSendResult.Create do
try
FCmdResult := CmdResult;
SendResult(Self);
finally
Free;
end; //with TSyncSendResult try finally
Disconnect;
end; //try Synchronize
Synchronize(formMain.Disconnected);
finally
Free;
end; // with TIdSMPT try
finally
LMsg.Free;
end; // try
Stop;
end;


Поток создается при помощи метода Create, которому передается фактический параметр типа Boolean. Если этот параметр имеет значение True, он должен активизироваться после передачи данных полям и таким свойствам, как FreeOnTerminate, с помощью метода Resume. У класса TidThread метод Create может быть перекрыт. По умолчанию в качестве параметра ему передается True. Можно выполнить вызов этого метода с параметром False, если нет необходимости передавать какие-либо данные в экземпляр класса. Ниже приводится код, описывающий обработчик события нажатия кнопки:
procedure TformMain.butnSendMailClick(Sender: TObject);


begin
butnSendMail.Enabled := False;
with TSMTPThread.Create do begin
FreeOnTerminate := True;
OnTerminate := ThreadTerminated;
FFrom := Trim(editFrom.Text);
FMessage := memoMsg.Lines.Text;
FRecipient := Trim(editTo.Text);
FSMTPServer := Trim(editSMTPServer.Text);
FSubject := Trim(editSubject.Text);
Start;
end;
end;
procedure TformMain.ThreadTerminated(ASender: TObject);
var
s: string;
begin
s := TIdThread(ASender).TerminatingException;
if Length(s) > 0 then
ShowMessage('An error occurred while sending message. ' + s);
else
ShowMessage('Message sent!');
butnSendMail.Enabled := True;
end;


Заметьте, что метод ThreadTerminated, который используется как обработчик события OnTerminate, проверяет свойство TerminatingException типа string. Если при работе потока происходит исключение, то в свойство TerminatingException записывается диагностическое сообщение и работа потока завершается. Обработчик события может, как в нашем случае, вывести на дисплей либо диагностическое сообщение, либо сообщение пользователю о том, что почта отправлена, либо выполнить какие-нибудь другие действия.
Часто бывает необходимо модернизировать главную форму приложения из потоков, созданных клиентскими компонентами. В нашем примере клиент модифицирует список на главной форме, который содержит динамическую информацию о состоянии соединения типа: связь с сервером установлена; подтверждение о передаче почты; разрыв соединения.
Если бы экземпляр клиента был создан в основном потоке приложения, модификация информации была бы стандартной. Однако, если вы работаете с CLX или VCL внутри вторичного потока, вам следует использовать метод Synchronize, фактическим параметром которого должна быть процедура без параметров, содержащая операторы чтения и записи свойств визуальных компонентов или вызовы их методов. Метод Synchronize обеспечивает вызов процедуры, переданный ему в качестве параметра в основном потоке, обеспечивая безопасный способ обращения к методам и свойствам визуальных компонентов. В нашем приложении используется два различных способа синхронизации.
Первая методика использует традиционную технику применения метода Synchronize. Например, следующий оператор вызывает один из методов компонента главной формы приложения:
Synchronize(formMain.Connected);
Поскольку процедура, адрес которой передается методу Synchronize, не может иметь параметров, то для преодоления этого ограничения операторы чаще всего помещают обращения к визуальным компонентам в «обертку» в виде дополнительной процедуры, например, как показано ниже:


procedure TformMain.Connected;
begin
Status('Connected');
end;
Метод Connected, вызываемый через Synchronize, в свою очередь, вызывает метод Status, являющийся методом класса главной формы:
procedure TformMain.Status(AMsg: string);
begin
lboxStatus.ItemIndex := lboxStatus.Items.Add(AMsg);
end;


Вторая методика использует синхронизирующий класс — вспомогательный класс для промежуточного хранения информации, которая затем используется при управлении визуальными компонентами в основном потоке.
• Имеется три способа объявления синхронизирующего класса:
• Объявить один или несколько полей для временного хранения данных.
• Объявить один или несколько методов, которые будут модифицировать визуальные компоненты.
• Объявить метод, входные параметры которого указывают на вторичный поток или метод вторичного потока, выполняемый через метод Synchronize.
Синхронизирующие классы особенно полезны, когда имеется большое количество данных либо когда необходима синхронизация между двумя или более вторичными потоками. Можно создавать экземпляр синхронизирующего класса в пределах потока, заполнять поля, вызывать его методы, а затем удалять экземпляр. Пример такого класса:


type
TSyncSendResult = class
private
FCmdResult: string;
procedure ShowResult; //a TThreadMethod type
public
procedure DoSynchronize(AThread: TIdThread; AMethod:
TThreadMethod);
end;

procedure TSyncSendResult.DoSynchronize(AThread: TIdThread;
AMethod: TThreadMethod);
begin
AThread.Synchronize(AMethod);
end;

procedure TSyncSendResult.ShowResult;
begin
formMain.Status('Mail accepted ok.');
formMain.Status('Server said ' + FCmdResult);
end;
Yoio eeann eniieucoaony a iaoiaa Run, eae iieacaii a neaao?uai eiaa.
with TSyncSendResult.Create do
try
FCmdResult := CmdResult;
DoSynchronize(Self, ShowResult);
finally
Free;
end; //with TSyncSendResult try

Построение собственного сетевого протокола
Первым шагом при разработке клиент-серверных приложений является выбор протокола. В зависимости от потребностей это могут быть HTTP, FTP или любые другие протоколы. Если ни один из стандартных протоколов не подходит, необходимо разрабатывать собственный. На первый взгляд эта задача кажется сложной, но это не так. Большинство протоколов строятся на обмене текстовой информацией. За командой (как правило, это текстовая строка) от клиента к серверу следует «квитанция», подтверждающая получение команды сервером, затем может следовать передача данных. Другими словами, протокольные соглашения похожи на разговор двух людей, каждый из которых руководствуется определенными правилами.
При разработке собственного протокола необходимо определить:
• структуру команд для сервера;
• возможные отклики сервера на полученные команды;
• формат передаваемых данных.
В качестве иллюстрации рассмотрим конкретное клиент-серверное приложение, обеспечивающее кластерные вычисления. Задачу сформируем следующим образом: на основании двух списков (списка IP-адресов с номерами портов и списка заданий) при помощи одной программы, которую мы будем называть клиентом, необходимо получить возможность удаленного запуска выполняемых файлов из списка заданий при помощи заранее размещенных и запущенных на удаленных компьютерах серверов. В итоге множество выполняемых файлов можно рассматривать как одну сетевую программу, контролируемую приложением-клиентом. Сфера применения приложений такого типа весьма широка. Речь может идти о трудоемких вычислениях (когда мощности одного процессора не хватает для решения задачи), или о решении задач системного администрирования (например, о поддержке целостности ЛВС), или о простом интегрирующем средстве (существует много приложений, которые необходимо объединить в нечто похожее на систему). По-видимому, можно придумать и другие способы применения. В операционной системе UNIX существует множество приложений, обеспечивающих подобную функциональность, к тому же большинство из них распространяется бесплатно. Однако до последнего времени в ОС Windows 95/98/Me/NT/2000/XP подобные инструментальные средства отсутствовали.
Клиент-серверное приложение, к рассмотрению которого мы переходим, состоит из двух EXE-файлов, предназначенных для организации параллельной пакетной обработки в локальной сети под управлением операционных систем Windows 95/98/NT/2000/XP с протоколом TCP/IP и WinSock (вся обработка осуществляется в рамках стандартных протоколов благодаря использованию компонентов TIdTCPClient и ТIdTCPServer). Комплекс свободен для использования (freeware), распространяется с исходным текстом. Предполагается, что обмен данными между EXE-файлами и вывод результатов работы сетевой программы осуществляются стандартными сетевыми средствами, например на сетевой диск.
Сервер запуска должен уметь запускать и завершать процесс по команде диспетчера и уведомлять диспетчер о завершении процессов. Диспетчер занимается распределением задач по компьютерам (процессорам). Данные и программы хранятся на файловом сервере или могут на время работы программы копироваться на обрабатывающий компьютер. При использовании пакетной обработки изменения в программе минимальны — обычно это вынесение границ внешнего цикла в параметры программы. При этом не нужно использовать какую-либо библиотеку. Следовательно, программы, вызываемые серверами, могут быть написаны на любом языке программирования. Другим достоинством системы является ее высокая надежность: для работы системы достаточно надежной работы одного диспетчера. Еще одно достоинство системы — возможность одновременного выполнения нескольких заданий от разных пользователей, что облегчает совместное использование ресурсов ЛВС.
Программу-сервер следует запустить на каждой ПЭВМ, где намереваются вести обработку. Эта программа обязательно должна иметь один параметр — уникальный номер порта (целое число, начиная от 500). Второй параметр (необязательный) — время искусственной задержки в миллисекундах для обеспечения синхронизации между EXE-файлами (по умолчанию 2000). При постоянной эксплуатации данного комплекса рекомендуется внести запуск сервера (или серверов) в меню Start каждой ПЭВМ или оформить их как сервисы Windows NT/2000/XP:
server.exe НомерПорта[, ВремяИскусственнойЗадержки]
• Программа-клиент должна находиться на компьютере, с которого будет осуществляться управление пакетом. Она имеет следующие параметры:
• Имя файла со списком программ для запуска (может отсутствовать; по умолчанию "adr_best.cfg").
• Имя файла со списком IP-адресов ЛВС (может отсутствовать; по умолчанию "prm_best.cfg").
• Время искусственной задержки (необязательный параметр).
Программа-клиент (диспетчер) управляется при помощи меню или кнопок, находящихся в верхней части окна. С помощью кнопки Запустить программу в сети или опции меню Начать обработку сетевая программа запускается на выполнение. Посредством кнопки или опции меню Сохранить параметры можно записать в файлы изменения, произведенные в списках программ с параметрами для запуска и адресов с номерами портов.
Строка списка программ для запуска состоит из имени EXE-файла с сетевым путем и параметрами через разделитель. Строка списка IP-адресов состоит из IP-адреса вычислительного узла сети и номера WinSock-порта, уникального в рамках одной ПЭВМ. Списки можно редактировать из программы-клиента: клавиша Ins — вставить новый элемент в список; клавиша Del — удалить элемент. Допускается редактирование списков любым автономным средством, например редактором текстового файла. На рисунке 1 приведена форма программы-клиента с заданием на запуск трех одинаковых стандартных программ калькулятор на двухпроцессорной ПЭВМ с IP-адресом 127.0.0.1 и портами 501 и 502.

Выход из программы-клиента осуществляется нажатием соответствующей кнопки или выбором опции меню.
Программа-сервер стартует в минимизированном виде. Для завершения работы необходимо, находясь в окне программы, нажать правую клавишу мыши и выбрать опцию всплывающего окна Close или нажать Alt-F4. Фрагменты кода клиента и сервера представлены в листинге 1 и листинге 2 соответственно.
Суть протокола обмена данными между клиентом и сервером очень проста. Клиент посылает серверу команду, содержащую строку с именем выполняемого файла, который серверу необходимо вызвать. В строке могут быть знаки, отделяющие имя выполняемого файла от возможных параметров, с которыми приложение вызывается на выполнение . Сервер осуществляет необходимые действия и в качестве отклика для экземпляра клиента посылает произвольную текстовую строку, единственное предназначение которой — синхронизация действий, выполняемых клиентским приложением. Ниже приводится отвечающий за эту функциональность код сервера, в котором подчеркнуты операторы обмена между клиентом и сервером:


with AThread.Connection do "
try
Command := ReadLn; // ожидание команды от клиента
{старт вторичного процесса и ожидание его завершения}
GetStartupInfo(StartupInfo);
CreateProcess(Nil,PChar(LCommand),Nil,Nil,False,
CREATE_DEFAULT_ERROR_MODE,Nil,Nil,StartupInfo,ProcessInfo);
WaitForSingleObject(ProcessInfo.hProcess,INFINITE);
ExitCode:=0;
GetExitCodeProcess(ProcessInfo.hProcess,ExitCode);
WriteLn('0'); // отклик для синхронизации на клиентском приложении
finally
Disconnect;
end; //with


Собственно, это почти что весь серверный код. Если бы в нем не было оператора WriteLn('0'), то в клиентской части необходимо было бы организовывать синхронизацию (ожидание завершения запущенного на серверной ПЭВМ задания) какими-либо другими средствами. В нашем случае клиент просто посылает команду посредством оператора WriteLn и ожидает получения данных при помощи ReadLn, причем с полученными данными он ничего не делает:


with IdTCPClient do E
begin
Connect;
try
WriteLn(FCustomerInfo); // команда для клиента
S := ReadLn; // ожидание отклика от сервера
Synchronize(UpdateList);
finally
Disconnect;
end; // try finally
sleep(TimeSleep);
end; //with


Сеанс связи между экземпляром клиента и сервером заключается в передаче клиентом серверу содержимого поля FcustomerInfo типа string. Сервер записывает эти данные в переменную Lcommand, также типа string. Выполнив необходимые действия, сервер передает клиенту фиктивную строку ‘0’, которую клиент помещает в рабочую переменную S.
Поскольку клиентская программа, согласно постановке задачи, отвечает за множественные соединения с серверами, она выполнена как многопоточное приложение. Отличие от вышерассмотренного случая состоит в том, что в методе Execute отсутствует какое-либо «зацикливание» и не требуется никакой терминальной обработки. Код написан линейно:


ThreadArray[i] := TTCPClientThread.Create(True);
with ThreadArray[i] do
begin

Port := StrToInt(S);
CustomerInfo := IntToStr(i)+' '+CheckListBox2.Items[i];
Resume;

end; //with


Создается экземпляр клиентского класса с отложенным выполнением; полям объекта передаются данные, необходимые для работы; управление передается методу Execute.
Основная сложность клиентского кода сосредоточена в процедуре DoIt, определяющей стратегию сетевой обработки. Процедура состоит из цикла повторяющихся действий. На основании списка IP-адресов с номерами портов вычисляется количество сетевых узлов, которые будут участвовать в обработке, и создается необходимое количество экземпляров клиентских классов. В качестве параметров им передаются данные из списка заданий на выполнение. Управление передается методом Execute для всех экземпляров, а затем ожидается их завершение с последующим высвобождением захваченных ресурсов.
Стратегия обработки строится следующим образом. Предполагается, что количество элементов в списке адресов всегда меньше или равно количеству элементов списка заданий; в противном случае список адресов искусственно обрезается. Если количество элементов в списках различается в разы, организуется единовременная обработка порций в соответствии с длиной списка адресов. Последняя порция может быть меньше, если длины списков не делятся нацело. Таким образом, поддерживается своего рода волнообразное заполнение предоставляемых ресурсов.
В статье «Параллельные алгоритмы» приводится описание «пускача удаленных заданий» в клиент-серверных системах, построенных на сокетах. Приложения имеют аналогичный визуальный интерфейс, но строятся на сообщениях Windows, которые они направляют сами себе при помощи функции PostMessage, а также на компонентах TClientSocket и TServerSocket — для общения друг с другом. К достоинствам данной системы можно отнести небольшой размер и простоту кода клиента. Недостатков у нее, по сравнению с только что рассмотренным подходом, намного больше. Во-первых, при одновременном обращении несколько серверов к клиенту (диспетчеру) нарушается его функциональность, что в ряде случаев подрывает надежность работы системы. Во-вторых, если время обработки одного узла оказалось меньше времени загрузки вычислительной ЛВС, происходит потеря вычислительных узлов, поскольку работают только компьютеры с IP-адресами, которые стоят первыми в списке. В-третьих, если попытаться воспользоваться системой при отсутствии сетевой поддержки, появится сообщение, изображенное на рисунке 2, что мешает проведению отладки.

В-четвертых, поскольку в компоненты TClientSocket и TserverSocket не встроена поддержка блокирующих вызовов, то отсутствует потенциальная возможность использования одних и тех же серверов для обеспечения работы различных экземпляров приложения-клиента.
Сравнение означенных двух вариантов исполнения клиент-серверного приложения, обеспечивающего кластерные вычисления, позволяет проанализировать методы решения сетевых задач, характерные для UNIX и Windows. Дело в том, что Windows 3.11 была построена на принципах корпоративной многозадачности, требующей для своего нормального функционирования организации событийного управления в приложениях, например, при помощи функции PostMessage. В то время подходы, используемые в UNIX, подвергались критике со стороны апологетов Windows за их, якобы, медлительность. И действительно: в среднем быстрее передать управление обработчику какого-то события, реализовав в нем короткий код, чем выполнять блокирующий вызов, напоминающий файловые операции. Однако при этом проблемы синхронизации приложения в целом ложатся на плечи разработчика. Времена изменились — ОС Windows давно уже поддерживает вытесняющую многозадачность. Прежние споры забываются, но груз старых технологических решений остается. Таким образом, на примере использования сетевых компонентов Delphi 6 можно наблюдать возможность фактического использования двух различных технологий программирования.
Подводя итог обсуждения функциональности рассмотренных приложений, необходимо отметить, что в них решены не все проблемы, которые могут возникать в приложениях подобного рода. Так, не поддерживается возможность удаления запущенных заданий; безопасность (в смысле доступа к ресурсам) ничем не обеспечивается. Но эта тема следующих публикаций.

По материалам: www.irportal.org.
Copyright © 2006-09.