Версия для печатиКомпоненты Indy, применяемые в Delphi 6. Часть 2

Юрий Зачесов

Применение многопоточности

Построение собственного сетевого протокола

Применение многопоточности

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

Для начала строится модуль с классом-наследником 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  
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;  

Вторая методика использует синхронизирующий класс — вспомогательный класс для промежуточного хранения информации, которая затем используется при управлении визуальными компонентами в основном потоке.

Имеется три способа объявления синхронизирующего класса:

  1. Объявить один или несколько полей для временного хранения данных.
  2. Объявить один или несколько методов, которые будут модифицировать визуальные компоненты.
  3. Объявить метод, входные параметры которого указывают на вторичный поток или метод вторичного потока, выполняемый через метод 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;  
Этот класс используется в методе Run, как показано в следующем коде.  
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 Н омерПорта[, ВремяИскусственнойЗадержки]

Программа-клиент должна находиться на компьютере, с которого будет осуществляться управление пакетом. Она имеет следующие параметры:

  1. Имя файла со списком программ для запуска (может отсутствовать; по умолчанию "adr_best.cfg").
  2. Имя файла со списком IP-адресов ЛВС (может отсутствовать; по умолчанию "prm_best.cfg").
  3. Время искусственной задержки (необязательный параметр).

Программа-клиент (диспетчер) управляется при помощи меню или кнопок, находящихся в верхней части окна. С помощью кнопки Запустить программу в сети или опции меню Начать обработку сетевая программа запускается на выполнение. Посредством кнопки или опции меню Сохранить параметры можно записать в файлы изменения, произведенные в списках программ с параметрами для запуска и адресов с номерами портов.

Строка списка программ для запуска состоит из имени EXE-файла с сетевым путем и параметрами через разделитель. Строка списка IP-адресов состоит из IP-адреса вычислительного узла сети и номера WinSock-порта, уникального в рамках одной ПЭВМ. Списки можно редактировать из программы-клиента: клавиша Ins — вставить новый элемент в список; клавиша Del — удалить элемент. Допускается редактирование списков любым автономным средством, например редактором текстового файла. На рис. 1 приведена форма программы-клиента с заданием на запуск трех одинаковых стандартных программ калькулятор на двухпроцессорной ПЭВМ с IP-адресом 127.0.0.1 и портами 501 и 502.

Выход из программы-клиента осуществляется нажатием соответствующей кнопки или выбором опции меню.

Программа-сервер стартует в минимизированном виде. Для завершения работы необходимо, находясь в окне программы, нажать правую клавишу мыши и выбрать опцию всплывающего окна Close или нажать Alt-F4. Фрагменты кода клиента и сервера представлены в листинге 1 и листинге 2 соответственно.

Суть протокола обмена данными между клиентом и сервером очень проста. Клиент посылает серверу команду, содержащую строку с именем выполняемого файла, который серверу необходимо вызвать. В строке могут быть знаки, отделяющие имя выполняемого файла от возможных параметров, с которыми приложение вызывается на выполнение . Сервер осуществляет необходимые действия и в качестве отклика для экземпляра клиента посылает произвольную текстовую строку, единственное предназначение которой — синхронизация действий, выполняемых клиентским приложением. Ниже приводится отвечающий за эту функциональность код сервера, в котором подчеркнуты операторы обмена между клиентом и сервером:

  with AThread.Connection do  
  try  
    LCommand := 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  
    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 для всех экземпляров, а затем ожидается их завершение с последующим высвобождением захваченных ресурсов.

Стратегия обработки строится следующим образом. Предполагается, что количество элементов в списке адресов всегда меньше или равно количеству элементов списка заданий; в противном случае список адресов искусственно обрезается. Если количество элементов в списках различается в разы, организуется единовременная обработка порций в соответствии с длиной списка адресов. Последняя порция может быть меньше, если длины списков не делятся нацело. Таким образом, поддерживается своего рода волнообразное заполнение предоставляемых ресурсов.

В статье «Параллельные алгоритмы» (см. КомпьютерПресс № 3’2001) приводится описание «пускача удаленных заданий» в клиент-серверных системах, построенных на сокетах. Приложения имеют аналогичный визуальный интерфейс, но строятся на сообщениях Windows, которые они направляют сами себе при помощи функции PostMessage, а также на компонентах TClientSocket и TServerSocket — для общения друг с другом. К достоинствам данной системы можно отнести небольшой размер и простоту кода клиента. Недостатков у нее, по сравнению с только что рассмотренным подходом, намного больше. Во-первых, при одновременном обращении несколько серверов к клиенту (диспетчеру) нарушается его функциональность, что в ряде случаев подрывает надежность работы системы. Во-вторых, если время обработки одного узла оказалось меньше времени загрузки вычислительной ЛВС, происходит потеря вычислительных узлов, поскольку работают только компьютеры с IP-адресами, которые стоят первыми в списке. В-третьих, если попытаться воспользоваться системой при отсутствии сетевой поддержки, появится сообщение, изображенное на рис. 2, что мешает проведению отладки.

В-четвертых, поскольку в компоненты TClientSocket и TserverSocket не встроена поддержка блокирующих вызовов, то отсутствует потенциальная возможность использования одних и тех же серверов для обеспечения работы различных экземпляров приложения-клиента.

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

Подводя итог обсуждения функциональности рассмотренных приложений, необходимо отметить, что в них решены не все проблемы, которые могут возникать в приложениях подобного рода. Так, не поддерживается возможность удаления запущенных заданий; безопасность (в смысле доступа к ресурсам) ничем не обеспечивается. Но эта тема следующих публикаций.

КомпьютерПресс 4'2002

1999 1 2 3 4 5 6 7 8 9 10 11 12
2000 1 2 3 4 5 6 7 8 9 10 11 12
2001 1 2 3 4 5 6 7 8 9 10 11 12
2002 1 2 3 4 5 6 7 8 9 10 11 12
2003 1 2 3 4 5 6 7 8 9 10 11 12
2004 1 2 3 4 5 6 7 8 9 10 11 12
2005 1 2 3 4 5 6 7 8 9 10 11 12
2006 1 2 3 4 5 6 7 8 9 10 11 12
2007 1 2 3 4 5 6 7 8 9 10 11 12
2008 1 2 3 4 5 6 7 8 9 10 11 12
2009 1 2 3 4 5 6 7 8 9 10 11 12
2010 1 2 3 4 5 6 7 8 9 10 11 12
2011 1 2 3 4 5 6 7 8 9 10 11 12
2012 1 2 3 4 5 6 7 8 9 10 11 12
2013 1 2 3 4 5 6 7 8 9 10 11 12
Популярные статьи
КомпьютерПресс использует