Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

DCOM (курсовая работа)

.pdf
Скачиваний:
59
Добавлен:
28.06.2014
Размер:
190.4 Кб
Скачать

Московский Энергетический Институт (Технический Университет)

Кафедра Прикладной Математики

Курсовой Проект

по курсу «Проектирование ПОАС»

«DCOM»

Студент:

Ясенков Е.М.

Группа:

А-13-03

Научный руководитель: Меньшикова К.Г.

Москва 2007

Введение

Distributed Component Object Model (DCOM) - программная архитектура, разработанная компанией Microsoft для распределения приложений между несколькими компьютерами в сети. Программный сервер на одной из машин может использовать DCOM для передачи сообщения (его называют удаленным вызовом процедуры) к серверу на другой машине. DCOM автоматически устанавливает соединение, передает сообщение и возвращает ответ удаленного сервера. DCOM является развитием СОМ.

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

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

Вдальнейшем будем опираться на следующую терминологию:

Компонент - составная часть распределенного приложения.

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

Remote Procedure Call (RPC) - удаленный вызов процедуры - сообщение, посылаемое по сети, которое позволяет программе, установленной на одном компьютере, инициировать выполнение необходимой операции на другом.

Некоторые особенности DCOM

Независимость от языка

Общим вопросом при проектировании и разработке распределенного приложения является выбор языка или инструмента для данного сервера. Язык обычно выбирают с учетом затрат на разработку, с учетом имеющейся квалификации и необходимого быстродействия. Как развитие СОМ, DCOM абсолютно не зависит от языка. Теоретически для создания СОМ-серверов может использоваться любой язык и эти серверы могут использоваться большим числом языков и инструментов. Java, Microsoft Visual C++, Microsoft Visual Basic, Microsoft Visual C#, Borland Delphi,

Borland C++ Builder - все они взаимодействуют с DCOM.

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

Управление соединением

DCOM управляет соединением клиентов к серверу используя счетчик ссылок. Когда клиент соединяется с сервером, DCOM увеличивает значение счетчика ссылок. Когда клиент разрывает соединение, DCOM уменьшает значение счетчика ссылок. Если значение счетчика достигает нуля – клиент свободен.

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

Во многих случаях поток информации между сервером и его клиентами не однонаправлен: серверу нужно инициировать некоторые действия со стороны клиента, например, извещение о том, что длительный процесс завершился, обновляются данные, просматриваемые пользователем (обзор новостей), или поступило следующее сообщение в чате или многопользовательской игре. Многие протоколы усложняют процесс осуществления такого типа симметричной связи. В DCOM каждый

2

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

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

Создание удаленного объекта

Сервисы создания объектов — одни из важнейших сервисов, предоставляемых СОМ. Клиенты обычно создают объекты, вызывая библиотеку СОМ или через моникеры (моникер — это имя определённого экземпляра объекта, имя одного конкретного сочетания некоторого CLSID и перманентных данных). Эти подходы работают и в DCOM, хотя и с некоторыми новыми особенностями. Рассмотрим различные варианты создания объектов, доступные клиентам.

Использование CoCreatelnstance

Независимо от того, где исполняется объект, клиент обычно создает его и затем получает указатели на необходимые интерфейс. Для большинства описанных ранее объектов — реализованных сервером "в процессе" или локальным сервером — это можно сделать, вызвав CoCreateInstance, а затем с помощью QueryInterface запросить указатели на нужные интерфейсы. Клиент может создать объект на удаленной машине, вызвав ту же самую функцию, т.е. клиенту даже не требуется знать, что объект выполняется на другом компьютере. Чтобы создать удаленный объект, клиент вызывает CoCreateInstance, как обычно, передавая CLSID вместе с IID, указывающим первый интерфейс, указатель на который ему нужен.

Однако для удаленного объекта необходимо задать дополнительный элемент — машину, на которой он должен быть создан. Для объекта, создаваемого на той же машине, системный реестр отображает CLSID в имя DLL или исполняемого файла, который должен быть загружен для данного класса. А при создании объекта на удаленной машине системный реестр может отображать CLSID в имя машины, на которой этот объект должен создаваться. Для создания удаленного объекта устанавливается связь с удаленной машиной, в ее реестре отыскивается данный CLSID, и на этой удаленной машине запускается соответствующий сервер. Если удаленный объект реализован в DLL, то запускается суррогатный процесс, просто загружающий DLL (данная возможность не поддерживается в первой версии DCOM, но ее планируется реализовать как можно быстрее.). Иначе запускается процесс объекта, как и в случае локального сервера.

Рассмотрим несколько упрощенную картину создания удаленного объекта с помощью CoCreateInstance. Клиент вызывает библиотеку СОМ для создания объекта с CLSID X, запрашивая указатель на интерфейс А этого объекта. Запись в реестре для CLSID X на клиентской машине содержит имя другого компьютера. DCOM предоставляет несколько вариантов идентификации удаленных машин в зависимости от сетевых протоколов, применяемых для доступа к удаленной системе. DCOM поддерживает доменные имена, используемые TCP/IP, а также адреса IP (Internet Protocol), имена NetBIOS и имена, применяемые NetWare IPX/SPX. Независимо от способа идентификации устанавливается связь с удаленной машиной, и там создается объект с учетом информации о CLSID Х реестра удаленной машины. Удаленная машина запустит сервер, а затем попросит его фабрику класса создать объект и вернуть указатель на интерфейс А. Этот указатель далее возвращается клиенту как обычно. Для клиента все выглядит аналогично процессу создания нового объекта локально.

Как уже упоминалось, CoCreateInstance вызывает CoGetClass Object, чтобы получить фабрику данного класса, а затем вызывает метод этой фабрики IClassFactory::CreateInstance для создания объекта на локальной машине. Подобный процесс применяется и при создании объекта на удаленной машине, хотя бы с точки зрения программиста. На самом же деле этот процесс был оптимизирован для повышения производительности, и все эти действия выполняются за один цикл взаимодействия "запрос-ответ" с удаленной машиной.

3

Использование CoCreatelnstanceEx

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

С этой целью DCOM предоставляет функцию CoCreateInstanсеЕх, альтернативную

CoCreateInstance. Как и CoCreateInstance, CoCreateInstanceEx позволяет клиенту задать CLSID класса объекта, который он хочет запустить. Но если СоСrеаteInstance допускает указание только одного IID, задающего первый нужный интерфейс, то CoCreateInstanceEx дает клиенту возможность задать список IID. После запуска объекта CoCreatelnstanceEx запрашивает у него указатели на все интерфейсы из этого списка и возвращает их клиенту одновременно. Вместо того, чтобы заставлять клиент многократно вызывать QueryInterface для получения указателей на интерфейсы объекта, одновременный возврат всех этих указателей может значительно ускорить процесс. И хотя CoCreatelnstanceEx создана для работы с удаленными объектами, нет причин, по которым клиенты не могли бы использовать ее для эффективного создания экземпляров объектов, реализованных локальными серверами и серверами "в процессе".

CoCreateInstanceEx также имеет параметр, позволяющий клиенту указать машину, на которой должен быть создан объект. Вместо того, чтобы полагаться в определении удаленной системы на локальный реестр, клиент может динамически выбирать удаленную машину во время создания объекта. Имя машины задается, как и в предыдущем случае, —доменным именем, адресом IP или в другом формате, поддерживаемом сетевыми протоколами. Поскольку CoCreateInstanceEx, как и CoCreateInstance, использует CoGetClassObject для получения указателя на интерфейс соответствующей фабрики класса, постольку имя машины необходимо передать при вызове CoGetClassObject. Для этого используется зарезервированный ранее параметр, так что необходимость в новой функции

CoGetClassObjectEx отпадает.

Объединение создания и инициализации

После того, как объект создан клиентом, следующим шагом обычно является инициализация объекта путем выдачи ему команды загрузить перманентные данные. Например, клиент может вызвать метод IPersistFile::Load вновь созданного объекта для загрузки данных из файла. Если объект хранит свои перманентные данные, используя структурированное хранилище, клиент может вызвать IPersistStorage::Load, передав указатель на соответствующее хранилище.

Этот двухэтапный процесс создания и затем инициализации при использовании удаленного объекта не претерпевает никаких изменений — клиент по-прежнему выполняет оба шага и волен делать это традиционным способом, вызвав сначала СоCreateInstance или CoCreateInstanceEx и обратившись затем к соответствующему методу интерфейса IPersist для инициализации объекта. Но если объект выполняется на удаленной машине, то выполнение этих шагов требует серии циклов "запрос-ответ", что может быть неприемлемо медленно. Чтобы улучшить ситуацию, DCOM предоставляет клиентам две альтернативные функции, каждая из которых создает и инициализирует объект за один прием. Если объект выполняется локально, то использование этих функций представляет собой главным образом лишь дополнительное удобство (хотя некоторый выигрыш в производительности достигается и здесь за счет сокращений числа вызовов между процессами), но для удаленных объектов реализация этих функций оптимизирована.

Первая функция — CoGetInstanceFromFile — создает новый объект и инициализирует его данными из файла. Параметры функции включают машину, на которой создается объект, CLSID, имя файла и, подобно CoCreateInstanceEx, список IID нужных клиенту интерфейсов. Если CLSID не задан, функция пытается определить его по имени файла так, как это делает файловый моникер. Использование этой функции аналогично вызову CoCreateInstanceEx и последующему обращению к методу IPersistFile::Load объекта. Вторая функция — CoGetInstanceFromIStorage — работает сходным образом за исключением того, что ей передается указатель на IStorage (он задает соответствующее

4

хранилище), а не имя файла. Вызов второй функции аналогичен вызову CoCreateInstanceEx с последующим обращением к методу IPersistStorage::Load объекта.

Хотя обе функции позволяют указать машину, на которой должен быть создан объект, ни для той, ни для другой эта информация не обязательна. Если имя машины не задано, место создания объекта зависит от нескольких факторов. Когда в локальном реестре данный CLSID связан с именем удаленной машины, как было описано выше, объект создается на указанном компьютере. А если запись для класса в реестре содержит значение ActivateAtStorage, то объект создается на той машине, где расположен заданный файл или хранилище.

Возможно также, что машина, на которой вызывается CoGetInstanceFromFile или CoGetInstanceFromIStorage, вообще не имеет информации о данном классе — локальный реестр может не содержать записи для соответствующего CLSID. Тогда предпринимается попытка создать объект на той машине, где находится указанный файл или хранилище. Если и в реестре той машины нет никакой информации о данном классе, вызов функции завершается с ошибкой. В противном случае объект создается на той же машине, где находится файл или хранилище, как если бы в локальном реестре было значение ActivateAtStorage.

Использование моникера

Чтобы создать удаленный объект, клиент может воспользоваться и моникером — объектом, который знает, как создать и инициализировать один экземпляр другого объекта. Моникеры могут выполнять создание экземпляров и инициализацию объектов как на локальных, так и на удаленных машинах. Для клиента моникера невидимо, исполняется созданный моникером объект локально или удаленно. Место, где выполняется объект, может быть невидимо и самому моникеру.

Когда клиент вызывает для моникера IMoniker::BindToObject, последний обычно вызывает CoCreateInstance с CLSID, полученным из своих перманентных данных. Затем моникер инициализирует вновь созданный объект, используя информацию своих перманентных данных — например, имя файла. Если для CLSID, передаваемого моникером CoCreateInstance, в реестре указана удаленная машина, то объект будет создан на этой машине. При этом сам моникер не узнает, что он создал удаленный объект.

Но моникер может быть в курсе того, что создает объект на удаленном компьютере. При вызове клиентом метода моникера IMoniker::BindToObject есть вероятность, что перманентное хранилище объекта, указываемого моникером, находится на удаленной машине и что в реестре клиентской машины для класса объекта указано ActivateAtStorage. В этом случае моникер создает объект на той машине, где находится перманентное хранилище объекта, подобно CoGetInstanceFromFile

и СоGetInstanceFromIStorage.

Файловый моникер содержит имя файла, которое обычно задает и местонахождение перманентных данных объекта (файл) и класс объекта (определяется по расширению имени файла или, возможно, по содержимому файла). Если данный файл хранится на удаленном файл-сервере, а не на локальной машине и если в реестре локальной машины присутствует ActivateAtStorage, моникер создаст объект на файл-сервере, а не на локальном компьютере.

Моникер URL содержит URL, определяющий местонахождение перманентного хранилища объекта. Если в локальном реестре для класса объекта, идентифицируемого этим моникером URL, задано ActivateAtStorage, то вызов метода моникера IMoniker::BindToObject создаст объект на компьютере, указанном URL, а не на машине, где исполняются моникер и/или его клиент. Затем, моникер приказывает объекту загрузить его перманентные данные по информации, заданной URL. И,

подобно CoGetInstanceFromFile и CoGetInstanceFromIStorage, файловый моникер или моникер URL

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

Доступ к удаленному объекту

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

5

Объектный RPC

После запуска удаленного объекта и получения указателей его интерфейсов клиент может вызывать методы этих интерфейсов. Вызов метода объекта "в процессе", по сути, означает непосредственное обращение к нему через виртуальную таблицу (а в случае диспинтерфейса и через IDispatch::Invoke). Обращение к методу объекта, реализованного в локальном сервере, требует участия заместителя, заглушки и некоторого механизма коммуникаций между процессами.

Вызов метода объекта, реализованного в удаленном сервере, также использует заместитель и заглушку, но в данном случае клиенту необходимо выполнить вызов удаленной процедуры (RPC) сервера. Протоколов RPC хватает в избытке, и Microsoft решила не создавать новый, но адаптировать существующий. Этот протокол — Microsoft называет его MS RPC — заимствован из OSF DCE (Microsoft, однако, не заимствовала у OSF реализацию протокола; MS RPC представляет собой другую реализацию DCE RPC, созданную на основе общедоступной спецификации). Как сам MS RPC, так и его модификация для DCOM — объектный RPC (Object RPC) — посылают по сети информацию в том же формате, что и DCE RPC (т.е. используют ту же структуру пакета). Хотя ORPC включает ряд новых соглашений по взаимодействию клиента с сервером, добавляет несколько новых типов данных и использует некоторые поля пакета особым образом, сама структура пакетов осталась прежней.

DCE RPC и MS RPC на самом деле включают в себя два разных протокола, которые поддерживаются и ORPC. Один из них — CN или СО — используется поверх протоколов с установлением логических соединений (connection-oriented protocols), таких как TCP (Transmission Control Protocol). Поскольку CN подразумевает, что нижележащий протокол гарантирует надежную доставку данных, то он не проверяет точность передачи. Другой протокол — DG или CL — используется поверх транспорта без установления логического соединения (также называемого протоколом дейтаграмм), такого как UDP (User Datagram Protocol). Рассматривая нижележащий протокол как совершенно ненадежный, DG реализует собственные механизмы, гарантирующие надежную доставку данных. Для выдающего запрос клиента оба протокола выглядят совершенно одинаково, хотя нижележащие транспортные протоколы ведут себя абсолютно по-разному. Эти различия скрываются соответствующим протоколом RPC.

Независимо от используемого протокола клиент должен обладать информацией связывания (binding information) с пунктом назначения, прежде чем он выполнит вызов ORPC. В составе этой информации обычно сетевой адрес удаленной машины (например, адрес IP) и указание, какая комбинация протоколов должна использоваться (например, CL RPC и UDP). Информация связывания может включать точку назначения транспорта (transport endpoint) — ее часто называют портом — задающую конкретный процесс на удаленной машине. Информацию связывания удобно представлять как строковое связывание (string binding) — символьной строкой, содержащей всю необходимую информацию.

Информацию связывания с данной удаленной системой клиент может получить по-разному. Например, имя машины, передаваемое функции CoCreateInstanceEx, может быть использовано для определения по крайней мере части информации связывания. Кроме того, возможна передача информации связывания (или ссылки на нее) от одного объекта другому. Чтобы понять, как это работает и почему это важно, сначала необходимо разобраться с ролью, которую играют OXID и разрешатели OXID.

OXID и разрешатели OXID

Сервер, реализующий один или несколько объектов СОМ, доступных клиентам на других машинах, можно рассматривать как экспортер объектов (object exporter). Разрешая удаленным клиентам доступ к своим объектам, сервер в некотором смысле "экспортирует" эти объекты, обеспечивая их межмашинное использование. Если сервер — однопоточный процесс с одним или несколькими объектами, то в качестве экспортера выступает сервер в целом. Если же сервер — многопоточный процесс с одной или несколькими комнатами (apartment), содержащими различные объекты, то экспортером является каждая из комнат. В любом случае каждому экспортеру объектов присваивается 8-байтовое значение — идентификатор экспортера объектов (OXID — Object Exporter Identifier). Заметьте: у одного процесса с несколькими комнатами может быть несколько OXID.

6

Для доступа к удаленному объекту клиент вначале должен получить его OXID (как это делается, объясняется в следующем разделе.) Имея OXID, клиент может использовать разрешатель OXID для отображения OXID в информацию связывания с объектом. На любой машине, поддерживающей DCOM, имеется разрешатель OXID (OXID resolver), и каждый разрешатель OXID поддерживает интерфейс lObjectExporter (название этого интерфейса, уходящее своими корнями в историю DCOM, несколько неудачно, так как он не реализуется экспортером объектов). Несмотря на свое СОМ-подобное имя, IObjectExporter не является интерфейсом СОМ. На самом деле это интерфейс RPC, и обращение к нему выполняется через вызовы чистого RPC, а не вызовы ORPC. В его составе 3 "метода": ResolveOXID, SimplePing и ComplexPing.

Каждый разрешатель OXID поддерживает таблицу OXID и их строковых связывании. Как минимум, данная таблица содержит запись для каждого исполняющегося на данной машине объекта, имеющего клиента вне своего процесса или комнаты. Она обычно включает и некоторые OXID и строковые связывания для объектов, исполняющихся на других машинах. Чтобы понять почему, рассмотрим, как осуществляется передача указателей на интерфейсы между машинами.

OBJREF: передача указателей интерфейсов

Как и вызов локального метода, вызов удаленного метода может содержать параметры. Но разные машины иногда используют для представления одних и тех же данных разные форматы. Например, многие системы для представления символов применяют ASCII, тогда как мэйнфреймы IBM применяют для этого код EBCDIC. А разные компьютеры используют разные форматы представления целых и вещественных чисел. Для обеспечения взаимодействия машин, применяющих разные форматы данных, выполняется маршалинг параметров вызова ORPC с использованием сетевого формата NDR

(Network Data Representation). NDR — стандартная часть DCE RPC (и, конечно же, MS RPC) —

обеспечивает эффективный способ передачи параметров практически любых типов СОМ IDL между машинами, использующими разные представления этих типов данных. Необходимая трансляция параметров из NDR в локальное представление выполняется машиной, принимающей вызов.

Однако один тип параметров, часто передаваемый в вызовах ORPC, не имеет прямой поддержки NDR — это указатели на интерфейсы. Объекты часто передают указатели на интерфейсы своим клиентам, а клиент имеет право передать имеющийся у него указатель на интерфейс любому другому объекту. Когда указатель на интерфейс ссылается на объект в том же процессе, проблем не возникает: указатель передается, как есть. Когда указатель на интерфейс ссылается на объект в другом процессе на той же машине, передается ссылка на данный интерфейс внутри соответствующего процесса. Но когда указатель на интерфейс ссылается на объект, расположенный на другой машине, то, что должно быть передано, является весьма сложной конструкцией — объектной ссылкой (object reference — OBJREF).

Согласно протоколу DCOM в состав OBJREF входят:

OXID;

уникальный идентификатор данного интерфейса объекта — идентификатор указателя интерфейса (interface pointer identifier — IPID);

идентификатор объекта (object identifier — OID), определяющий объект;

строковое связывание для разрешателя OXID на той машине, где исполняется объект.

После передачи OBJREF от одного объекта другому принимающий объект может вызывать

методы с помощью указателя на интерфейс, представляемого данной OBJREF.

Но как объект, получающий указатель на интерфейс, определяет, как связаться с объектом, заданным этим указателем? Точнее, откуда берется информация связывания с объектом, не являющаяся частью самой OBJREF? Есть два варианта. Если любой объект на локальной машине уже связывался недавно с объектом, имеющим тот же OXID, что и объект, на который ссылается указатель, то данный OXID уже есть в таблице локального разрешателя OXID. Если же информация связывания в таблице отсутствует, ее надо запросить у машины, на которой исполняется объект, заданный данной OBJREF.

Чтобы получить необходимую для использования новой OBJREF информацию связывания, DCOM на машине клиента, получившего OBJREF, обращается за помощью к локальному разрешателю OXID, передавая ему эту OBJREF. (Все это, конечно, скрыто от программиста и является частью инфраструктуры, обеспечиваемой DCOM.) Разрешатель OXID выделяет OXID из OBJREF и пытается отыскать его в собственной таблице OXID. Если объект с тем же OXID уже используется кем-то на этой

7

машине, то в таблице будет запись для данного OXID с необходимым строковым связыванием. Предположим, объект А получил OBJREF интерфейса, поддерживаемого объектом X, расположенным на другой машине. Чтобы получить информацию связывания для осуществления вызовов с помощью нового указателя, реципиент передает OBJREF своему локальному разрешателю OXID. Разрешатель OXID может сразу возвратить нужную информацию, если она уже есть в его таблице.

Однако допустим, что OBJREF ссылается на интерфейс удаленного объекта Q, для которого у локального разрешателя OXID информации нет. Тогда разрешатель OXID извлекает из OBJREF информацию связывания с разрешателем OXID на машине, где исполняется объект Q, и с помощью ее связывается с этим разрешателем. Для этого локальный разрешатель OXID вызывает метод ResolveOxid интерфейса lObjectExporter разрешателя OXID на удаленной машине. В качестве параметра этого вызова передается OXID, выделенный из только что полученной OBJREF. Данный вызов возвращает строковое связывание для данного OXID, которое затем добавляется локальным разрешателем в свою таблицу. Вновь полученный указатель на интерфейс теперь можно использовать.

Здесь возникает естественный вопрос: почему в составе OBJREF передается не строковое связывание самого объекта, но связывание разрешателя OXID на машине объекта? Ведь тогда клиентскому разрешателю OXID вообще не пришлось бы связываться с разрешателем OXID объекта — полученная OBJREF уже содержала бы все необходимое для связи с объектом.

Чтобы понять, почему архитекторы DCOM приняли иное решение, вспомним, что DCOM позволяет клиенту работать с удаленным объектом с помощью разных протоколов: TCP/IP, UDP/IP, IPX/SPX и др. Для каждого поддерживаемого объектом протокола может потребоваться загрузка отдельной DLL, которой обычно необходимы дополнительные потоки для ожидания поступления запросов по данному протоколу. Так что объекту, работающему с несколькими протоколами, это может дорого обойтись. Не удивительно, что архитекторы DCOM стремились снизить накладные расходы, загружая протоколы только тогда, когда это необходимо, и пытаясь обойтись их минимальным количеством.

С этой целью объекты используют отложенную регистрацию протоколов (lazy protocol registration). Другими словами, объект загружает необходимый для протокола код, лишь когда клиент захочет работать с ним по данному протоколу. Например, когда разрешатель OXID объекта получает от клиентской машины вызов IObjectExporter::ResolveOxid, этот запрос выполняется с помощью протокола, скажем, TCP/IP. Разрешатель OXID на машине объекта может определить, загружен ли уже объектом код TCP/IP. Если нет, разрешатель приказывает объекту загрузить соответствующий код, после чего клиент и объект могут взаимодействовать по TCP/IP. Если затем другой разрешатель OXID запросит связь с тем же объектом по UDP/IP, объект получит указание загрузить код и этого протокола. В то время как разрешатели OXID обязаны ожидать вызовы по всем протоколам, поддерживаемым данной машиной, конкретный объект загружает код только протоколов, явно запрошенных его клиентами. Данный подход иногда требует дополнительного обмена данными при установлении соединения, но он позволяет объектам избежать напрасной траты ресурсов на не используемые ими протоколы.

Роль SCM

При создании объекта на той же машине, что и клиент, библиотека СОМ этой машины предоставляет выполнение основной работы по запуску соответствующего сервера диспетчеру управления сервисами (Service Control Manager — SCM). Когда же объект создается на удаленной машине, SCM клиентской машины должен в свою очередь делегировать выполнение этой задачи SCM удаленной машины. Для обеспечения стандартной схемы, позволяющей SCM связываться со своим аналогом на удаленной машине, все SCM поддерживают интерфейс IActivation.

Иначе говоря, SCM запрашивает создание объекта у SCM на другой машине через интерфейс IActivation. Очень простой интерфейс IActivation содержит единственную операцию — RemoteActivation, используемую для активизации объектов, создаваемых любыми описанными ранее способами.

Оптимизация IUnknown

Каждый объект СОМ обязан поддерживать IUnknown — это не менее справедливо для объектов, доступ к которым осуществляется по сети. Но если бы каждый вызов клиентом метода

8

lUnknown транслировался непосредственно в вызов ORPC объекта, результатом почти наверняка была бы неприемлемая производительность. В связи с этим DCOM обеспечивает оптимизацию этих важных методов.

В основе этой оптимизации лежат объекты OXID (OXID objects), каждый из которых поддерживает интерфейс IRemUnknown. Как уже отмечалось, OXID идентифицирует группу объектов, с которыми можно связаться через данное строковое связывание. Все такие объекты представлены одним объектом OXID и, следовательно, одним IRemUnknown. Все удаленные вызовы методов всех IUnknown таких объектов вначале обрабатываются интерфейсом IRemUnknown соответствующего объекта OXID.

Интерфейс IRemUnknown аналогичен, но не эквивалентен IUnknown. Как и IUnknown, он содержит 3 метода: RemQueryInterface, RemAddRef и RemRelease. Но в отличие от методов IUnknown

они позволяют выполнять групповые запросы. Например, одним вызовом RemQueryInterface можно запросить несколько указателей на интерфейсы нескольких объектов. Аналогично RemAddRef и RemRelease позволяют одновременно увеличивать и уменьшать счетчики ссылок нескольких интерфейсов нескольких объектов (доступ к каждому из которых осуществляется с помощью одной и той же информации связывания).

Клиенты не используют интерфейс IRem Unknown непосредственно. Вместо этого они, как всегда, вызывают методы обычного IUnknown. Реализация этих методов стандартным заместителем группирует вызовы и затем выполняет (минимально возможное количество раз) удаленные вызовы IRemUnknown. Например, вызовы клиентом AddRef и Release обычно не транслируются в вызовы

IRemUnknown::RemAddRef и IRemUnknown::RemRelease один к одному. Вместо этого каждый из этих методов IRemUnknown вызывается не более одного раза — после первого вызова AddRef и после последнего вызова Release. Значит, объект могут использовать несколько клиентов, и в то же время его счетчик ссылок может быть равен 1. И никаких проблем: ведь объект не будет разрушен пока его счетчик ссылок не равен 0. С точки зрения объекта и его клиентов, все работает аналогично локальному случаю, но в распределенной среде это решение гораздо эффективнее.

Хотя оптимизация вызовов IUnknown, выполняемая инфраструктурой СОМ, несомненно полезна, иногда и сам клиент может действовать более разумно. Например, когда ему нужны указатели на несколько интерфейсов данного удаленного объекта, быстрее всего получить их за один вызов. Для этой цели DCOM определяет интерфейс IMultiQI, указатель на который клиент получает обычным способом — вызывая QueryInterface через указатель на любой интерфейс. Но IMultiQI обычно реализуется локальным заместителем, а не удаленным объектом, так что создается иллюзия, что его поддерживает любой объект. Этот простой интерфейс содержит один метод (кроме унаследованных от IUnknown) — QueryMultipIeInterfaces. Клиент может передать ему список идентификаторов интерфейсов и в ответ получить указатель на каждый из них. Вместо того, чтобы заставлять клиента выполнять несколько удаленных вызовов, вся информация получается в результате одного запроса к удаленному объекту.

Тестовый опрос

Все объекты СОМ используют подсчет ссылок для определения момента, когда они могут безопасно себя разрушить. Но в случае объекта, выполняющегося на удаленной машине, есть одна проблема: что будет, если исполнение клиента завершится внезапно? Когда объект и его клиент выполняются на одной машине, объект может быть уведомлен о внезапной смерти клиента непосредственно. Но в распределенном случае ситуация несколько сложнее.

К счастью, эта проблема давно решена: клиент может производить периодический тестовый опрос (pinging) каждого используемого им объекта путем выдачи ему вызова ORPC. Если в течение достаточно длительного времени от данного клиента не приходит подобный тест, клиент считается "погибшим", и объект может предпринять соответствующие действия. Тестовый опрос может служить простым способом поддержки подсчета ссылок в распределенной среде.

Но тестовый опрос может быть и невероятно неэффективным. Вообразите, например, что на машине работают 10 клиентов и у каждого из них по 30 указателей на интерфейсы 10 разных объектов, причем последние все расположены на второй машине. Наивный (и очень дорогой) метод тестового опроса может требовать от каждого клиента опрашивать все интерфейсы каждого объекта по отдельности. Разумнее, объединив эти опросы, обеспечить гораздо более эффективное использование сети.

9

Так и поступает DCOM. Вместо того, чтобы требовать от каждого клиента опрашивать все объекты индивидуально, пакеты тестового опроса отправляются и принимаются разрешателями OXID. Разрешатель OXID определяет, какие интерфейсы относятся к одному и тому же объекту и какие объекты находятся на одной машине. Затем он определяет набор тестового опроса (ping set), включающего в себя все интерфейсы этих объектов, обращаясь к IObjectExporter::ComplexPing разрешателя OXID удаленной машины. После этого, разрешатель OXID может выполнять один тестовый опрос другого разрешателя для всего набора с помощью IObjectExporter::SimplePing. Это гораздо эффективнее посылки пакета опроса каждым клиентом каждому объекту и уж тем более — тестового опроса каждого интерфейса каждого объекта.

Тестовый пример:

Разработаем тестовый пример приложения, где будут использоваться возможности DCOM, а именно связь приложения с сервером Microsoft Excel на другой машине в Локальной Сети. Среда разработки – Borland C++ Builder.

Создадим новый проект. Добавим на форму пару компонентов – Button1::TButton и DCOMConnection1::TDCOMConnection. Последний – устанавливает связь с указанным приложениемсервер указанной машины через DCOM.

Для начала выделим значок компонента DCOMConnection1 и настроим его используя Object

Inspector:

В поле ComputerName нужно ввести адрес (IP-адрес или DNS-имя) машины, на которой мы запустим сервер Microsoft Excel;

В поле ServerName вводится имя сервера автоматизации, введем «Excel.Application». Тогда в поле ServerGUID появится GUID сервера «Excel.Application», взятый из реестра данной машины.

После настройки компонента DCOMConnection1, можно написать обработчик события при нажатии на кнопку Button1:

void __fastcall TForm1::Button1Click(TObject *Sender)

{

DCOMConnection1->Connected = true; //включаем соединение

App = DCOMConnection1->AppServer; //получаем настройки сервера из DCOMConnection1 App.OlePropertySet("Visible", true); //создаем видимое окно сервера vVarBooks=App.OlePropertyGet("Workbooks"); //создаем файл Excel с 1 листом App.OlePropertySet("SheetsInNewWorkbook",1);

vVarBooks.OleProcedure("Add");

vVarBook=vVarBooks.OlePropertyGet("Item",1);

vVarSheets=vVarBook.OlePropertyGet("Worksheets");

vVarSheet=vVarSheets.OlePropertyGet("Item",1); vVarSheet.OlePropertySet("Name","Тест"); //назовем этот лист «Тест» vVarCell=vVarSheet.OlePropertyGet("Range","A1:Z99"); //настроим шрифт, выравнивание vVarCell.OlePropertyGet("Font").OlePropertySet("Size",60); vVarCell.OlePropertySet("HorizontalAlignment",-4108); vVarCell.OlePropertySet("VerticalAlignment",-4108); vVarCell=vVarSheet.OlePropertyGet("Cells").OlePropertyGet("Item",2,8); vVarCell.OlePropertySet("Value","Превед, DCOM!"); //введем некоторый текст в наш файл

}

Итак, тестовое приложение создано. Протестируем приложение в локальной сети. Запустим его на машине с IP-адресом 172.16.3.143, а Excel будем запускать с машины с IP-адресом 172.16.3.51

В итоге при нажатии кнопки в созданном приложении на машине (172.16.3.143), на другой машине (172.16.3.51) запускается Excel и создается файл.

Ограничения

Для работы тестового примера, на обеих машинах должны быть «одинаковые» учетные записи, т.е. имя пользователя и пароль должны совпадать, а также обе учетные записи должны быть с правами «Администратора». Так же на обоих компьютерах должны быть отключены все имеющиеся программы класса «Firewall» («Брандмауэр»), используемые для защиты подключений к компьютеру по локальной сети и интернет.

10