- •Розподілені системи обробки інформації
- •Передмова
- •Розділ 1. Огляд компонентних технологій створення розподілених програмних систем
- •1.1. Узагальнена архітектура і механізм функціонування об'єктних розподілених систем
- •1.2. Основні приклади технологій створення розподілених систем
- •1.3. Переваги використання розподілених технологій
- •Розділ 2. Розроблення розподілених систем на основі модели com/dcom у Delphi
- •2.1. Використання dll у Delphi
- •2.1.1. Поняття dll
- •2.1.2. Створення dll у середовищі Delphi (експорт)
- •2.1.3. Використання dll у Delphi (імпорт)
- •2.1.4. Створення динамічних бібліотек для редагування ресурсів
- •2.2. Основи сом-технології
- •2.2.1. Загальний опис
- •2.2.2. Базові поняття
- •2.2.3. Бібліотека сом
- •2.2.4. Бібліотека типів
- •2.3.2. Сервер сом у Delphi
- •2.3.3. Бібліотека типів у delphі
- •2.4. Створення системи клієнт-сервер на основі базового com-об’єкту у складі внутрішнього сервера
- •2.4.1. Створення сом-сервера
- •2.4.2. Створення сом-клієнта
- •2.4.3. Використання сом-об’єкту в клієнтській програмі
- •2.5. Механізм міжпроцесного обміну
- •2.6. Створення систем клієнт-сервер на основі зовнішнього базового сом-об’єкту
- •2.6.1. Основні поняття
- •2.6.2. Засоби організації потокової взаємодії клієнта і сервера
- •2.6.3. Методи формування екземпляра сом-об’єкту
- •2.6.4. Формування екземпляра зовнішнього сом-об’єкту
- •2.6.5. Створення сом-сервера
- •2.6.6. Створення сом-клієнта
- •2.7. Автоматизація
- •Створення сервера автоматизації;
- •2.7.1. Базові поняття
- •2.7.2. Сервер автоматизації
- •2.7.3. Контролер автоматизації
- •2.8. Створення системи клієнт-сервер на основі внутрішнього сервера автоматизації
- •2.8.1. Об'єкт автоматизації. Клас tAutoObject
- •2.8.2. Вбудований сервер автоматизації
- •2.8.3. Створення клієнта автоматизації
- •2.9. Зовнішній сервер автоматизації
- •2.9.1. Основні визначення
- •2.9.2. Виконання маршалінгу з рядками, шрифтами і зображеннями
- •2.9.3. Перетворення наявного застосунка в сом-сервер автоматизації
- •2.9.4. Створення клієнта автоматизації
- •2.10. Події в сом і зворотні виклики на основі інтерфейсів диспетчирування
- •2.10.1. Створення сервера автоматизації
- •3. Формування бібліотеки типів
- •4. Формування методів
- •5. Реєстрація сервера
- •2.10.2. Створення клієнтського застосунка
- •2.10.3. Підключення множини клієнтів до сервера
- •2.11. Інтерфейси зі зворотним викликом
- •2.11.1. Створення сервера
- •2.11.2. Створення клієнтського застосунка
- •2.12. Технологія ActiveХ
- •2.12.1. Використання готових елементів АctiveХ
- •2.12.2. Розроблення власних елементів АctiveХ
- •2.12.3. Поширення елементів керування ActiveХ і форм ActiveХForm у Web-середовище
- •2.14. Dcom технологія
- •2.14.1. Загальна схема взаємодії сом-клієнта і сом-сервера
- •2.14.2. Розроблення системи «клієнт-віддалений сом-сервер»
- •Розділ 3. Проектування розподілених систем на платформі Microsoft .Net
- •3.1.1. Здійсненя викликань з типів .Net до типів сом
- •3.1.2. Звернення клієнта сом до збірки .Net
- •3.2. Об’єктно-орієнтована архітектура .Net Remotіng – основа створення розподілених систем Mіcrosoft .Net.
- •3.2.1. Створення системи клієнт-сервер на основі технології Remoting
- •Розділ 4. Створення системи "клієнт - сервер" на основі технології corba
- •4.1. Загальні теоретичні відомості
- •4.2. Створення серверного застосунка
- •1. Створення файла опису інтерфейсу
- •Викликання конструктора створення corba сервера
- •Формуємо модуль Unit1
- •Формуємо реалізацію методу
- •4.3. Створення клієнтського застосунка
- •Викликання конструктора corba-клієнта
- •2. Формування форми
- •3. Запуск застосунка
- •Приклад програмних кодів сервера
- •4.4. Порівняльний аналіз технологій сом і соrва
- •4.4.1. Основні принципи об'єктних моделей
- •4.4.2. Об'єктні моделі
- •4.4.3. Підтримка операційних систем
- •4.4.4. Формальний опис архітектури і проблеми реалізації
- •4.4.5. Підсумки порівняння
- •Літературні джерела
2.11. Інтерфейси зі зворотним викликом
Замість того, щоб використовувати інтерфейси диспетчирування для повернення події клієнту, можна створити звичайний інтерфейс, і визначити в ньому методи зі зворотним викликом. Такий інтерфейс зі зворотним викликом оголошується в СОМ-сервері, а реалізується на стороні клієнта.
Розглянемо процес створення застосунків сервера і клієнта, у яких буде продемонстровано процес використання адаптованого інтерфейсу, що підтримує зворотнє викликання від сервера до клієнта, тобто виклик сервером на виконання методів, реалізованих у клієнтському застосунку.
2.11.1. Створення сервера
Загальна схема використання інтерфейсів диспетчирування для повернення події клієнту подана нижче.
-
Для демонстрації використання інтерфейсів диспетчирування будемо використовувати зовнішній сервер з об'єктом автоматизації IntfCallback.
-
До складу бібліотеки типів автоматично буде включено інтерфейс IIntfСallback та відповідній до нього диспетчерський інтерфейс IIntfСallbackDisp.
IIntfСallback = interface(IDispatch);
IIntfСallbackDisp = dispInterface;
-
За допомогою редактора бібліотеки типів необхідно додати ще один інтерфейс автоматизації – інтерфейс зворотного виклику IIntfCallbackEvents, для нього також буде створено відповідний диспетчерський інтерфейс IIntfCallbackEventsDisp.
IIntfCallbackEvents = interface(IDispatch)
IIntfCallbackEventsDisp = disІnterface
-
В інтерфейс прямого виклику необхідно додати методи для забезпечення підключення клієнта до сервера:
function Connect (Callback: IIntfCallbackEvents):integer;
Клієнт буде викликати цей метод для встановлення зв'язку з CОМ-об'єктом. Як параметр клієнт повинен передати цьому методу вказівник на свій інтерфейс методів зворотного виклику. Метод є функцією і повертає клієнту ціле число, що інтерпретується клієнтом як його номер – ідентифікатор унікальний серед усіх клієнтів, яких обслуговує сервер.
function Disconnect(IdClient: Integer): boolean;
Викликається клієнтом в разі відключення від сервера
procedure SendText(text:WideString);
Клієнт викликає цей метод для передавання рядка тексту сервера.
5. За допомогою редактора бібліотеки типів необхідно сформувати склад інтерфейсу зворотного виклику, тобто тих методів, які в цьому інтерфейсі тільки оголошені, але не реалізовані. Їхні імена починаються з On, наприклад OnТext(Text: WideString).
При необхідності сервер повинен викликати ці методи, сформувавши параметри, тобто здійснити зворотнє викликання.
Зауваження: CОМ-сервер має бути спроектований так, щоб він міг зберігати інформацію про підключених клієнтів у вигляді деякого списку, формував би для кожного клієнта його унікальний номер і вмів одержувати, зберігати і використовувати отримані від клієнта інтерфейси зворотного виклику.
Розглянемо докладніше розробленням сервера. Для створення застосунка сервера скористаймося традиційною методикою — треба почати в середовищі Delphi новий проект і додати до нього об'єкт автоматизації. Виберіть у меню Delphi команду FileNew, а потім, коли відкриється діалогове вікно New Items, виберіть у ньому вкладку ActiveX. Після цього двічі клацніть на елементі Automation Objects на вкладці ActiveX. Delphi виведе на екран діалогове вікно майстра Automation Object Wizard. Заповніть поля у цьому вікні. Назвемо клас IntfCallback і для нього буде сформовано базовий інтерфейс IIntfCallback.
Після цього на екран автоматично буде виведене вікно Type Library Editor. Перше, що потрібно буде в ньому зробити, це створити інтерфейс, що буде використовуватися для зворотного викликання в застосунку-клієнті. Використовуючи панель інструментів і інші засоби Type Library Editor, додайте новий інтерфейс з ім’ям IIntfCallbackEvents. Додайте в цей інтерфейс єдиний метод OnText. Оголошення цього методу наведене нижче:
procedure OnText (text: WideString); safecall;
Тепер додайте в інтерфейс IIntfCallback три методи — Connect, Disconnect і SendText. Оголошення цих методів подані нижче:
function Connect(const Callback: IIntfCallbackEvents):Integer;
function Disconnect (UserID: Integer):Boolean;
procedure SendText (Text: WideString);
Клацніть на кнопці Refresh Implementation (Обновити реалізацію) панелі інструментів і закрийте редактор Type Library Editor.
Примітка 1. Той сервер, який ми зараз створимо, з погляду кінцевого користувача буде цілком ідентичний розробленому у прикладі попереднього параграфа, але в ньому буде реалізована інша методика організації взаємодії застосунків сервера і клієнтів.
Інтерфейс IIntfCallbackEvents буде реалізований у клієнтському застосунку, тому зараз не будемо зупинятися на реалізації його методу OnText. Але методи Connect, Disconnect і SendText ми зараз розглянемо докладніше.
Ініціалізація сервера
Коли сервер запитується клієнтом, викликається метод Initialize класу сервера. Програмний код методу TIntfCallback.Initialize наведено нижче:
procedure TIntfCallback.Initialize;
begin
inherited Initialize;
//Додати одиницю до глобального лічильника підключень.
Inc(NumConnections);
// Обновити кількість підключень в екранній формі
if NumConnections = 1 then
Form1.label2.Caption := '(1 active connections)';
else
Form1.label2.Caption := '('+IntToStr(NumConnections)+' active connections)';
end;
Спочатку викликається метод Initialize базового класу. При цьому збільшується на одиницю кількість підключень до сервера. Змінна NumConnections є глобальною. Вона має бути глобальною, оскільки метод викликається при підключенні клієнта до сервера. Нам треба централізовано стежити за поточною кількістю клієнтів, підключених до цього сервера.
Після зміни кількості підключень відповідно оновлюється зображення в головній екранній формі застосунка.
Обробка підключень клієнтів
Клієнтські застосунки будуть викликати метод Connect класу сервера для встановлення зв'язку з ним.
Примітка 2. У назві методу Connect (Підключення) немає нічого магічного. З таким самим успіхом його можна назвати, наприклад, RegisterClient (Реєстрація клієнта).
Зазвичай бажання підключитися до сервера "висловлює" багато клієнтів. Тому метод Connect додає нового клієнта у внутрішній список клієнтів. Для цього створюється два допоміжних класи — TConn і TConns. Реалізація класів представлена в лістингу застосунка нижче за текстом.
Клас TConn відображає єдине підключення. Кожен клієнт повинен забезпечити реалізацію інтерфейсу IIntfCallbackEvents для сервера, що буде використовуватися сервером для викликання клієнта. Крім того, кожен клієнт має власний унікальний ідентифікатор, що використовується для його розпізнавання.
Клас TConns містить список об'єктів класу TConn, а також зберігає ідентифікатор, присвоєний останньому клієнту, що вступив у зв'язок із сервером. Коли до сервера підключається наступний клієнт, цей ідентифікатор збільшується на одиницю.
Зазначимо, що клас TConns має три методи, також названі Connect, Disconnect і SendText. Клас TIntfCallback, що реалізує інтерфейс IIntfCallback, просто передає керування однойменним методам класу TConns.
Виклик клієнта із сервера
Сервер звертається до клієнта шляхом викликання методу OnText інтерфейса IIntfCallbackEvents.
В об'єкті TConns міститься список усіх підключених клієнтів разом з посиланнями на їхні інтерфейси IIntfCallbackEvents. Тому можна, просто перебираючи елементи цього списку, викликати для кожного чергового клієнта метод інтерфейсу IIntfCallbackEvents. Як це робиться на практиці, показано в подальшому фрагменті програмного коду:
procedure TConns.SendText(Text: WideString);
var
Index: Integer;
C: TConn;
begin
for Index := 0 to FConns.Count - 1 do
begin
С:=TConn(FConns[index]);
C.FCallBack.OnText(Text);
end;
end;
У цій нескладній процедурі викликається метод OnText інтерфейсу IIntfCallbackEvents для всіх підключених клієнтів.
Вище ми зупинялися тільки на найбільш важливих фрагментах програмного коду сервера. Наведемо повний лістинг програмного коду сервера IntfSrv (файл IntfUnit.pas).
unit IntfUnit;
interface
uses
Windows, Classes, SysUtils, ComObj, ActiveX, IntfSrv_TLB;
type
// Клас, відповідальний за єдине підключення
TConn = class
public
FUserID: Integer;
FCallback: IIntfCallbackEvents;
destructor Destroy; override;
end;
// Клас, відповідальний за список поточних підключень
TConns = class
private
FConns: TList;
Сount: Integer;
public
constructor Create;
destructor Destroy; override;
function Connect(const Callback:IIntfCallbackEvents):Integer;
function Disconnect(UserID: Integer): Boolean;
procedure SendText(Text: WideString);
end;
// СОМ-об'єкт, до якого звертається клієнт
TIntfCallback = class(TAutoObject, IIntfCallback)
protected
function Connect(const Callback:IIntfCallbackEvents):Integer; safecall;
function Disconnect(UserID: Integer): WordBool; safecall;
procedure SendText(const Text: WideString); safecall;
public
procedure Initialize; override;
destructor Destroy; override;
function Connections: TConn;
end;
const
NumConnections: Integer =0;
var
GlobalConnections: TConns;
FLastUserID:Integer;
implementation
uses ComServ, MainForm;
{ TIntfCallback }
procedure TIntfCallback.SendText( const Text: WideString)
begin
// Передати текст через список підключень
Connections.SendText(Text);
end;
function TIntfCallback.Connect(const Callback: IIntfCallbackEvents):Integer;
//Додати нове підключення
begin
Result := Connections.Connect(Callback);
end;
destructor TIntfCallback.Destroy;
begin
// Зменшити на 1 кількість підключень,
Dec(NumConnections};
// Якщо не залишилося підключень, можна позбутися списку
if NumConnections = 0 then
begin
GlobalConnections.Free;
GlobalConnections:= nil;
end;
// Оновити кількість підключень в екранній формі
if NumConnections = 1 then
Form1.Label2.Caption := '(1 active connection)'
else
Form1.Label2.Caption:= '('+IntToStr(NumConnections)+'active connections)';
inherited Destroy;
end;
// Тут перевантажується метод Initialize класу TAutoObject.
// Метод можна використовувати замість конструктора
procedure TIntfCallback.Initialize;
begin
inherited Initialize;
// Додати одиницю до глобального лічильника підключень
Inc(NumConnections);
// оновити кількість підключень в екранній формі
if NumCоnnections = 1 then
Form1.Labe2.Caption := '(1 active connection)'
else
Form1.Label2.Caption:='('+IntToStr(NumConnections)+'active connections)';
end;
function TIntfCallback.Disconnect(UserID: Integer); WordBool;
begin
//Оброблення запиту на розрив підключення, що надійшов від //клієнта
Result := Connections.Disconnect(UserID);
end;
function TIntfCallback.Connections: TConns;
begin
// функція Connections повертає глобальний список підключень
if GlobalConnections = nil
then GlobalConnections := TConns.Create;
Result := GlobalConnections;
end;
{ TConn }
destructor TConn.Destroy;
begin
// Явно видаляється інтерфейс події
FCallback := nil;
inherited Destroy;
end;
{ TConns }
function TConns.Connect(const Callback: IIntfCallbackEvents): Integer;
var
C: TConn;
begin
// Присвоїти кожному підключенню унікальний ідентифікатор
Inc(Count);
// Сформувати нове підключення
С:= TConn.Create;
// Запам'ятати інтерфейс події
С.FCallback:= Callback;
// Встановити ID користувача
C.FUserID:= Count;
// Додати користувача в список
FConns.Add(C);
// Повернути ID, присвоєний користувачеві
Result:= Count;
end;
constructor TConns.Create;
begin
Count := 0;
FConns := TList.Create;
end;
destructor TConns.Destroy;
var
Index: Integer;
C: TConn;
Begin
for Index := 0 to Fconns.Count - 1 do
begin
C := TConn(FConns[Index]);
C.Free;
end;
FConns.Free;
end;
function TConns.Disconnect(UserID: Integer): Boolean;
var
Index: Integer;
C: TConn;
begin
Result := False;
for index := 0 to FConns.Count - 1 do
begin
C := TConn(FConns[Index]);
if C.FUserID = UserID then
begin
C.Free;
FConns.Delete(Index);
Result := True;
exit;
end;
end;
FConns.Count:= FConns.Count-1;
end;
procedure TConns.SendText(Text: WideString);
var
Index: Integer; C: TConn;
Begin
for Index := 0 to FConns.Count - 1 do
begin
C := TConn(FConns[Index]);
C.FCallback.OnText(Text);
end;
end;
initialization
// замініть параметр на ciSingleInstance, і тоді для кожного
// клієнта буде запускатися своя копія сервера
TAutoObjectFactory.Create(ComServer, TIntfCallback, Clas_IntfCallback, ciMultilnstance, tmApartment);
end.
Наведемо лістинг програмного коду модуля головної екранної форми застосунка IntfSrv.
unit MainForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, IntfUnit;
type
TForm1 = class(TForm)
Label1: TLabel;
Label2: TLabel;
private
{ Оголошення закритих членів}
public
{ Оголошення загальнодоступних членів }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
end.
Наведемо скорочений (без коментарів) лістинг програмного коду бібліотеки типів сервера, сформований Delphi.
c А бібліотеці типів
// • •
interface
uses Windows, ActiveX, Classes, Graphics, OleCtrls, StdVCL;
const
LIBID IntfSrv:TGUID ='{E9D7678H-F7E3-11D2-909B-0040F6741DE2}'; IID_IIntfCallback:TGUID='{E9D7678F-F7E3-11D2-909B-0040F6741DE2}';
CLASS IntfCallback:TGUID='{E9D76791-F7E3-11D2-909B-0040F6741DE2}';
IID_IIntfCallbackEvents:TGUID='{E9E76793-F7E3-11D2-909B-G040F6741DE2}';
type
IntfCallback = interface
IIntfCallbackDisp = dispinterface;
IIntfCallbackEvents =interface;
IIntfCallbackEventsDisp =dispinterface;
// Оголошення сполучених класів, визначених у бібліотеці типів //IntfCallback = IntfCallback;
// Interface: IntfCallback
// Flags: (4416) Dual OleAutomation Dispatchable
IntfCallback = interface(IDispatch)
['{E9D7678F-F7E3-11D2-909B-0040F6741DE2}']
procedure SendText{const Text: WideString); safecall;
function Connect(const Callback:IIinfCallbackEvents): Integer; safecall;
function Disconnect(UserID: integer): WordBool; safecall;
end;
//DispIntf: IIntfCallbackDisp
IIntfCallbackDisp = dispinterface
['{E9D7678F-F7E3-11D2-909B-0040F6741DE2}']
procedure SendText(const Text: WideString); dispid 1;
function Connect(const Callback; IIntfCallbackEvents): Integer; dispid 2;
function Disconnect(UserID: Integer); WordBool; dispid 3; end;
// Interface: IIntfCallbackEvents
// Flags: (4416) Dual OleAutomation Dispatchable
IIntfCallbackEvents = interface(IDispatch)
['{E9D76793-F7E3-11D2-909B-0040F6741DE2}']
procedure OnText(const Text: WideString); safecall;
end;
// DispIntf: IIntfCallbackEventsDisp
// Flags; (4416) Dual OleAutomation Dispatchable
IIntfCallbackEvents = interface(IDispatch)
['{E9D76793-F7E3-11D2-909B-0040F6741DE2}']
procedure OnText(const Text: WideString); safecall;
end;
IIntfCallbackEventDisp=dispinterface
['{E9D76793-F7E3-11D2-909B-0040F6741DE2}']
procedure OnText(const Text: WideString); dispid 1;
end;
CoIntfCallback = class
class function Create: IIntfCallback;
class function CreateRemote(const MachineName: string): IIntfCallback;
end;
implementation
uses ComObj;
class function CoIntfCallback.Create: IIntfCallback;
begin
Result:=CreateComObject(CLASS_IntfCallback) as IIntfCallback;
end;
class function CoIntfCallback.CreateRemote(const MachineName: string): IIntfCallback;
begin
Result:=CreateRemoteComObject(MachineName, CLASS_IntfCallback) as IIntfCallback;
end;
end.