Поддержка разработки распределенных приложений в...

211
Московский государственный технический университет им. Н. Э. Баумана Москва, 2006 Поддержка разработки распределенных приложений в Microsoft .NET Framework Учебный курс Горин С. В., Крищенко В. А.

Upload: -

Post on 29-Jul-2015

172 views

Category:

Documents


4 download

TRANSCRIPT

Page 1: Поддержка разработки распределенных приложений в Microsoft .NET

Московский государственный технический университет им. Н. Э. Баумана

Москва, 2006

Поддержка разработки распределенных приложений в Microsoft .NET Framework

Учебный курс

Горин С. В., Крищенко В. А.

Page 2: Поддержка разработки распределенных приложений в Microsoft .NET

Содержание

i

Содержание

Введение ....................................................................................................................................2

Тема 1. Введение в распределенные системы .................................................................4

1.1. Понятие распределенной системы ____________________________________ 4

1.2. Определение распределенной системы. Программные компоненты ________ 9

1.3. Требования к распределенным системам______________________________ 10

1.4. Понятие промежуточной среды _____________________________________ 14

Тема 2. Взаимодействие компонент распределенной системы ...................................18

2.1. Модели взаимодействия компонент распределенной системы____________ 18

2.2. Обмен сообщениями ______________________________________________ 19

2.3. Дальний вызов процедур ___________________________________________ 21

2.4. Использование удаленных объектов _________________________________ 23

Модель единственного вызова ......................................................................................26

Модель единственного экземпляра ..............................................................................27

Активация по запросу клиента......................................................................................28

Состояние компоненты распределенной системы ......................................................28

Использование свойств удаленных объектов ..............................................................29

2.5. Распределенные события___________________________________________ 32

2.6. Распределенные транзакции ________________________________________ 33

2.7. Безопасность в распределенных системах_____________________________ 35

2.8. Промежуточные среды в Microsoft .NET Framework ____________________ 36

Тема 3. Описание интерфейса программной компоненты ..........................................38

3.1. Сервисы и интерфейс программной компоненты_______________________ 38

3.2. Язык XML и схемы XML___________________________________________ 41

3.3. SOAP: язык сообщений распределенной системы ______________________ 44

3.4. WSDL: описание интерфейса программной компоненты ________________ 45

3.5. Выводы по описанию интерфейса компоненты ________________________ 49

Page 3: Поддержка разработки распределенных приложений в Microsoft .NET

Содержание

ii

Тема 4. Сериализация объектов. Способы сериализации в .NET Framework............51

4.1. Сериализация графа объектов_______________________________________ 51

4.2. Методы сериализации в .NET Framework _____________________________ 54

4.3. Класс сериализации XmlSerializer ___________________________________ 56

4.4. Классы сериализации SoapFormatter и BinaryFormatter __________________ 67

4.5. Выводы по использованию классов сериализации ______________________ 71

Тема 5. Microsoft Message Queuing (MSMQ) – промежуточная среда обмена

сообщениями...........................................................................................................................73

5.1. Служба обмена сообщениями MSMQ ________________________________ 73

5.2. Инфраструктура, необходимая для использования MSMQ _______________ 75

5.3. Применение службы сообщений MSMQ в распределенных системах______ 77

5.4. Использование очередей сообщений MSMQ в .NET Framework __________ 80

5.5. Выводы по использованию MSMQ __________________________________ 89

Тема 6. Промежуточная среда COM+ и служба Enterprise Services ...........................91

6.1. Введение в промежуточную среду COM+ _____________________________ 91

6.2. Сервисы COM+___________________________________________________ 93

Синхронизация ...............................................................................................................93

Балансировка нагрузки ..................................................................................................94

Just-in-time-активация и пул объектов..........................................................................94

Распределенные транзакции..........................................................................................95

Ожидающие компоненты ..............................................................................................97

Слабо связанные события ..............................................................................................98

Обеспечение безопасности ............................................................................................99

6.3. Использование среды COM+ в приложениях .NET Framework____________ 99

Взаимодействие среды COM+ и среды CLR ...............................................................99

Создание обслуживаемых компонент ........................................................................101

Регистрация обслуживаемых компонент ...................................................................104

Использование исключений в обслуживаемых компонентах..................................106

Создание компенсирующего менеджера ресурсов ...................................................107

Использование слабо связанных событий .................................................................116

Сервисы COM+ без компонент COM+.......................................................................120

Page 4: Поддержка разработки распределенных приложений в Microsoft .NET

Содержание

iii

6.4. Выводы по использованию среды Enterprise Services / COM+ ___________ 122

Тема 7. Веб-службы ASP.NET......................................................................................125

7.1. Введение в веб-службы ___________________________________________ 125

7.2. Использование расширения WSE ___________________________________ 128

7.3. Создание веб-службы в среде .NET Framework _______________________ 130

Описание веб-службы ..................................................................................................130

Создание посредника веб-службы ..............................................................................132

7.4. Реализация нестандартного расширения WSE ________________________ 133

7.5. Менеджер пользовательских записей________________________________ 138

7.6. Выводы по использованию веб-служб_______________________________ 148

Тема 8. Среда .NET Remoting .......................................................................................150

8.1. Введение в среду .NET Remoting ___________________________________ 150

8.2. Архитектура среды .NET Remoting _________________________________ 152

8.3. Конфигурирование среды .NET Remoting ____________________________ 156

8.4. Веб-службы в .NET Remoting ______________________________________ 160

8.5. Канал среды Remoting ____________________________________________ 163

8.6. Создание нестандартного канала ___________________________________ 170

8.7. Выводы по использованию .NET Remoting ___________________________ 178

Тема 9. Обеспечение безопасности распределенных систем в .NET Framework ....181

9.1. Введение в обеспечение безопасности_______________________________ 181

9.2. Механизмы обеспечения безопасности ______________________________ 183

Электронные сертификаты ..........................................................................................183

Протокол Kerberos........................................................................................................186

9.3. Безопасность промежуточных сред .NET Framework___________________ 187

9.4. Выводы по безопасности промежуточных сред _______________________ 189

Тема 10. Применение промежуточных сред .................................................................190

10.1. Взаимосвязь промежуточных сред __________________________________ 190

10.2. Сравнение технологий создания распределенных систем ______________ 191

10.3. Выводы по применению промежуточных сред .NET Framework _________ 194

Page 5: Поддержка разработки распределенных приложений в Microsoft .NET

Содержание

iv

Список использованной литературы ..................................................................................195

Приложение I. Администрирование каталога COM+.......................................................196

Приложение II. Использование ASP.NET без IIS..............................................................199

Приложение III. Симметричное шифрование....................................................................204

Page 6: Поддержка разработки распределенных приложений в Microsoft .NET

Введение

1

Сведения об авторах

Горин Сергей Викторович, доцент МГТУ им. Баумана, Москва, [email protected].

Крищенко Всеволод Александрович, к.т.н., доцент МГТУ им. Баумана, Москва,

[email protected].

Системные требования к курсу

1. Microsoft .NET Framework 2.0

2. Для работы всех примеров требуется Microsoft Windows XP Pro SP2, или Microsoft

Server 2003, или последующие операционные системы с установленными службами

MSMQ, COM+ и IIS

Благодарности

Курс создан при поддержке Microsoft Corporation в рамках программы SE Contest 2006.

Правовая информация

Microsoft и Microsoft Windows являются зарезервированными торговыми марками или

торговыми марками Microsoft Corporation, США. Прочие упомянутые в курсе названия

продуктов и организаций могут быть торговыми марками их владельцев.

Page 7: Поддержка разработки распределенных приложений в Microsoft .NET

Введение

2

Введение

В настоящее время много внимания уделяется технологиям разработки распределенных

приложений, охватывающих несколько независимых компьютеров. В течение

последних десяти лет было создано большое число технологий и стандартов,

использование которых должно было помочь разработчикам в создании

распределенных приложений масштаба предприятия. Однако поддержка многих

технологий была изначально достаточно трудоемкой и сложной для разработчиков

прикладных программ, использовавших классические языки программирования, такие

как C/С++.

Одной из задач, стоящих перед разработчиками Microsoft, создающими так

называемую общеязыковую инфраструктуру (Common Language Infrastructure, CLI), так

же известную как .NET, была наиболее полная поддержка средств разработки

распределенных систем. Поэтому в платформе разработки приложений Microsoft .NET

Framework имеется встроенная поддержка четырех взаимосвязанных технологий,

предназначенных для использования в распределенных системах: очередей сообщений

(messaging queues), объектов COM+, объектов .NET Remoting, веб-служб (web services).

Каждая из данных технологий имеет свои достоинства, недостатки и особенности

применения при построении распределенных приложений. Сделать осознанный выбор

с пользу той или иной технологии при решения конкретных прикладных задач трудно

без знакомства со всеми ними, также как и без базовых теоретических знаний о

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

следующие цели.

1. Ознакомить слушателей с общими вопросами создания распределенных систем,

рекомендациями по архитектуре сложных приложений.

2. Дать представление о технологиях создания распределенных приложений,

поддерживаемые платформой Microsoft .NET, показать их особенности и взаимосвязь.

3. Дать критерии выбора той или иной технологии при создании распределенных

систем, показать границы их применимости.

Page 8: Поддержка разработки распределенных приложений в Microsoft .NET

Введение

3

Курс рассчитан на студентов средних или старших курсов. Слушатели должны быть

знакомы с архитектурой Microsoft.NET Framework, а так же иметь представление об и

языке программирования C#, основных сетевых протоколах стека TCP/IP, основах

криптографии, теории графов и формальных языков.

Курс отражает точку зрения авторов на текущее (весна 2006 года) состояние

технологии разработки .NET и связанных с нею технологий. При разработке курса ни в

коем случае не ставилась цель подменить им техническую документацию.

Page 9: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 1. Введение в распределенные системы

4

Тема 1. Введение в распределенные системы

Дается представление о распределенных системах, компонентах распределенной

системы. Формулируются требования к распределенным системам. Вводится

понятие промежуточной среды.

1.1. Понятие распределенной системы

По утверждению известного специалиста в области информатики Э. Таненбаума, не

существует общепринятого и в то же время строгого определения распределенной

системы. Некоторые остряки утверждают, что распределенной является такая

вычислительная система, в которой неисправность компьютера, о существовании

которого пользователи ранее даже не подозревали, приводит к остановке всей их

работы. Значительная часть распределенных вычислительных систем, к сожалению,

удовлетворяют такому определению, однако формально оно относится только к

системам с уникальной точкой уязвимости (single point of failure).

Часто при определении распределенной системы во главу угла ставят разделение ее

функций между несколькими компьютерами. При таком подходе распределенной

является любая вычислительная система, где обработка данных разделена между двумя

и более компьютерами. Основываясь на определении Э. Таненбаума, несколько более

узко распределенную систему можно определить как набор соединенных каналами

связи независимых компьютеров, которые с точки зрения пользователя некоторого

программного обеспечения выглядят единым целым.

Такой подход к определению распределенной системы имеет свои недостатки.

Например, все используемое в такой распределенной системе программное

обеспечение могло бы работать и на одном единственном компьютере, однако с точки

зрения приведенного выше определения такая система уже перестанет быть

распределенной. Поэтому понятие распределенной системы, вероятно, должно

основываться на анализе образующего такую систему программного обеспечения.

Page 10: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 1. Введение в распределенные системы

5

Как основу описания взаимодействия двух сущностей рассмотрим общую модель

взаимодействия клиент-сервер, в которой одна сторон (клиент) инициирует обмен

данными, посылая запрос другой стороне (серверу). Сервер обрабатывает запрос и при

необходимости посылает ответ клиенту (рис. 1.1).

КлиентЗапрос

ОтветСервер

Рисунок 1.1. Модель взаимодействия клиент-сервер

Взаимодействие в рамках модели клиент-сервер может быть как синхронным, когда

клиент ожидает завершения обработки своего запроса сервером, так и асинхронным,

при котором клиент посылает серверу запрос и продолжает свое выполнение без

ожидания ответа сервера. Модель клиента и сервера может использоваться как основа

описания различных взаимодействий. Для данного курса важно взаимодействие

составных частей программного обеспечения, образующего распределенную систему.

Интерфейс пользователя

Логика приложения

Доступ к данным

Пользователь

База данных

Рисунок 1.2. Логические уровни приложения

Рассмотрим некое типичное приложение, которое в соответствие с современными

представлениями может быть разделено на следующие логические уровни (рис. 1.2):

Page 11: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 1. Введение в распределенные системы

6

пользовательский интерфейс (ИП), логика приложения (ЛП) и доступ к данным (ДД),

работающий с базой данных (БД). Пользователь системы взаимодействует с ней через

интерфейс пользователя, база данных хранит данные, описывающие предметную

область приложения, а уровень логики приложения реализует все алгоритмы,

относящиеся к предметной области.

Поскольку на практике разных пользователей системы обычно интересует доступ к

одним и тем же данным, наиболее простым разнесением функций такой системы между

несколькими компьютерами будет разделение логических уровней приложения между

одной серверной частью приложения, отвечающим за доступ к данным, и

находящимися на нескольких компьютерах клиентскими частями, реализующими

интерфейс пользователя. Логика приложения может быть отнесена к серверу,

клиентам, или разделена между ними (рис. 1.3).

Сервернаясоставляющая

системы

Клиентскаясоставляющая

системы

Интерфейс пользователяЛогика приложения

Логика приложенияДоступ к данным

База данных

Пользователь

Рисунок 1.3. Двухзвенная архитектура

Архитектуру построенных по такому принципу приложений называют

клиент-серверной или двухзвенной. На практике подобные системы часто не относят к

классу распределенных, но формально они могут считаться простейшими

представителями распределенных систем.

Развитием архитектуры клиент-сервер является трехзвенная архитектура, в которой

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

Page 12: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 1. Введение в распределенные системы

7

самостоятельные составляющие системы, которые могут работать на независимых

компьютерах (рис. 1.4).

Серверприложения

Сервербазы данных

Клиентская частьприложенияИнтерфейс пользователя

Логика приложения

Доступ к данным

База данных

Пользователь

Рисунок 1.4. Трехзвенная архитектура

Запрос пользователя в подобных системах последовательно обрабатывается клиентской

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

под распределенной системой понимают системы с более сложной архитектурой, чем

трехзвенная.

Партер попоставкам товаров

Системарозничнойпродажи

ИПЛП

Пользователь

ДДЛПДД

Системаонлайн-платежей

ЛПДД

БДБД БД

Рисунок 1.5. Распределенная система розничных продаж

Page 13: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 1. Введение в распределенные системы

8

Применительно к приложениям автоматизации деятельности предприятия,

распределенными обычно называют системы с логикой приложения, распределенной

между несколькими компонентами системы, каждая из которых может выполняться на

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

продаж должна использовать запросы к логике приложения третьих фирм, таких как

поставщики товаров, системы электронных платежей или банки, предоставляющие

потребительские кредиты (рис 1.5).

Таким образом, в обиходе под распределенной системой часто подразумевают рост

многозвенной архитектуры «в ширину», когда запросы пользователя не проходят

последовательно от интерфейса пользователя до единственного сервера баз данных.

В качестве другого примера распределенной системы можно привести сети прямого

обмена данными между клиентами (peer-to-peer networks). Если предыдущий пример

имел «древовидную» архитектуру, то сети прямого обмена организованы более

сложным образом, рис. 1.6. Подобные системы являются в настоящий момент,

вероятно, одними из крупнейших существующих распределенных систем,

объединяющие миллионы компьютеров.

Клиент

Управляющийсервер

ИПЛП

ПользовательДанные

ЛП

ДДЛП

Клиент

Управляющийсервер

ИПЛП

ПользовательДанные

ЛП

ДДЛП

Рисунок 1.6. Система прямого обмена данными между клиентами

Page 14: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 1. Введение в распределенные системы

9

1.2. Определение распределенной системы. Программные компоненты

В распределенных системах функции одного уровня приложения могут быть разнесены

между несколькими компьютерами. С другой стороны, программное обеспечение,

установленное на одном компьютере, может отвечать за выполнение функций,

относящихся к разным уровням. Поэтому подход к определению распределенной

системы, считающей ее совокупностью компьютеров, условен. Для описания и

реализации распределенных систем было введено понятие программной компоненты.

Программная компонента – это единица программного обеспечения, исполняемая на

одном компьютере в пределах одного процесса, и предоставляющая некоторый набор

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

как выполняющимися на этом же компьютере, так и на удаленных компьютерах. Ряд

компонент пользовательского интерфейса предоставляют свой сервис конечному

пользователю.

Компонентылогики

приложения

Компонентыинтерфейса

пользователя

Источникданных

Пользователи

Компонентыдоступа кданным

Источникданных

Пользователи

Рисунок 1.7. Компоненты распределенной системы

Page 15: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 1. Введение в распределенные системы

10

Применительно к программам с использованием платформы CLI, под процессом в

приведенном определении компоненты следует понимать домен приложения

(application domain), который можно рассматривать как аналог процесса в управляемом

коде.

Основываясь на определении программной компоненты, можно дать более точное

определение распределенной системы. Согласно нему, распределенная система есть

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

нескольких связанных компьютерах и выглядящих с точки зрения пользователя

системы как единое целое (рис. 1.7). Прозрачность является атрибутом распределенной

системы. При исправном функционировании системы от конечного пользователя

должно быть скрыто, где и как выполняются его запросы.

Программная компонента является минимальной единицей развертывания

распределенной системы. В ходе модернизации системы одни компоненты могут быть

обновлены независимо от прочих компонент.

В хорошо спроектированной системе функции каждой компоненты относятся только к

одному уровню приложения. Однако разделение только на три уровня представляется

недостаточным для классификации компонент. Например, часть компонент

пользовательского интерфейса могут взаимодействовать с пользователем, а часть –

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

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

являются общепринятыми и часто в значительной степени привязаны к приложениям

автоматизации деятельности предприятия, что все-таки не является синонимом

распределенной системы.

1.3. Требования к распределенным системам

Чтобы достигнуть цели своего существования – улучшения выполнения запросов

пользователя – распределенная система должна удовлетворять некоторым

необходимым требованиям. Можно сформулировать следующий набор требований,

которым в наилучшем случае должна удовлетворять распределенная вычислительная

система.

Page 16: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 1. Введение в распределенные системы

11

Открытость. Все протоколы взаимодействия компонент внутри распределенной

системы в идеальном случае должны быть основаны на общедоступных стандартах.

Это позволяет использовать для создания компонент различные средства разработки и

операционные системы. Каждая компонента должна иметь точную и полную

спецификацию своих сервисов. В этом случае компоненты распределенной системы

могут быть созданы независимыми разработчиками. При нарушении этого требования

может исчезнуть возможность создания распределенной системы, охватывающей

несколько независимых организаций.

Масштабируемость. Масштабируемость вычислительных систем имеет несколько

аспектов. Наиболее важный из них для данного курса – возможность добавление в

распределенную систему новых компьютеров для увеличения производительности

системы, что связано с понятием балансировки нагрузки (load balancing) на серверы

системы. К масштабированию относятся так же вопросы эффективного распределение

ресурсов сервера, обслуживающего запросы клиентов.

Поддержание логической целостности данных. Запрос пользователя в

распределенной системе должен либо корректно выполняться целиком, либо не

выполняться вообще. Ситуация, когда часть компонент системы корректно обработали

поступивший запрос, а часть – нет, является наихудшей.

Устойчивость. Под устойчивостью понимается возможность дублирования

несколькими компьютерами одних и тех же функций или же возможность

автоматического распределения функций внутри системы в случае выхода из строя

одного из компьютеров. В идеальном случае это означает полное отсутствие

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

к невозможности обслужить запрос пользователя.

Безопасность. Каждый компонент, образующий распределенную систему, должен

быть уверен, что его функции используются авторизированными на это компонентами

или пользователями. Данные, передаваемые между компонентами, должны быть

защищены как от искажения, так и от просмотра третьими сторонами.

Page 17: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 1. Введение в распределенные системы

12

Эффективность. В узком смысле применительно к распределенным системам под

эффективностью будет пониматься минимизация накладных расходов, связанных с

распределенным характером системы. Поскольку эффективность в данном узком

смысле может противоречить безопасности, открытости и надежности системы,

следует отметить, что требование эффективности в данном контексте является

наименее приоритетным. Например, на поддержку логической целостности данных в

распределенной системе могут тратиться значительные ресурсы времени и памяти,

однако система с недостоверными данными вряд ли нужна пользователям.

Желательным свойством промежуточной среды является возможность организации

эффективного обмена данными, если взаимодействующие программные компоненты

находятся на одного компьютере. Эффективная промежуточная среда должна иметь

возможность организации их взаимодействия без затрагивания стека TCP/IP. Для этого

могут использоваться системные сокеты (unix sockets) в POSIX-системах или

именованные каналы (named pipes).

Устойчивость распределенной системы связана с понятием масштабируемости, но не

эквивалентна ему. Допустим, система использует набор обрабатывающих запросы

серверов и один диспетчер запросов, который распределяет запросы пользователей

между серверами. Такая система может считаться достаточно хорошо масштабируемой,

однако диспетчер является уязвимой точкой такой системы. С другой стороны, система

с единственным сервером может быть устойчива, если существует механизм его

автоматической замены в случае выхода его из строя, однако она вряд ли относится к

классу хорошо масштабируемых систем. На практике достаточно часто встречаются

распределенные системы, не удовлетворяющие данным требованиям: например, любая

система с уникальным сервером БД, реализованным в виде единственного компьютера,

имеет уникальную точку сбоя. Выполнение требований устойчивости и

масштабируемости обычно связано с некоторыми дополнительным расходами, что на

практике может быть не всегда целесообразно. Однако используемые при построении

распределенных систем технологии должны допускать принципиальную возможность

создания устойчивых и высоко масштабируемых систем.

Классическим примером системы, в значительной мере отвечающей всем

представленным выше требованиям, является система преобразования символьных

Page 18: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 1. Введение в распределенные системы

13

имен в сетевые IP-адреса (DNS). Система имен – организованная иерархически

распределенная система, с дублированием всех функций между двумя и более

серверами (рис. 1.8).

Серверраспознавания

имен

Клиентскоеприложение

ИПЛП

Пользователь

ЛП

ДД

КорневойсерверЛП

ДД

ИерархияDNS-серверов

СерверзоныЛП

ДД

Сервер,отвественный за

домейн

ЛПДД

БД

БД

БД

БД

Рисунок 1.8. Система DNS

Запрос пользователя на преобразование имени (например, w3c.org) в сетевой адрес

передается серверу распознавания имен поставщика услуг интернета. Сервер

распознавания имен по очереди опрашивает серверы из иерархии службы имен. Опрос

начинается с корневых серверов, который возвращает адреса серверов, ответственных

за зону домена. Затем опрашивается сервер, ответственный за зону (в данном случае –

.org), возвращающий адреса серверов, ответственных за домен второго уровня, и так

далее. Серверы имен кешируют информацию о соответствии имен и адресов для

уменьшения нагрузки на систему. Программное обеспечение на компьютере

пользователя обычно имеет возможность соединиться с как минимум двумя

различными серверами распознавания имен.

Тем не менее, и в системе распознавания имен не все требования к распределенным

системам выполнены. В частности, она не содержит каких-либо явных механизмов

Page 19: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 1. Введение в распределенные системы

14

обеспечения безопасности. Это приводит к регулярным атакам на серверы имен в

надежде вывести их из строя, например, большим количеством запросов.

1.4. Понятие промежуточной среды

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

в нее машины являются удаленными вычислительными системами. Теоретической

основой сетевого взаимодействия удаленных систем является общеизвестная модель

взаимодействия открытых систем OSI/ISO, которая разделяет процесс взаимодействия

двух сторон на семь уровней: физический, канальный, сетевой, транспортный,

сеансовый, прикладной, представительский.

Система 1 Система 2

Программныекомпоненты

Операционнаясистема

ПромежуточнаясредаПредставительский

Сеансовый

Прикладной

Сетевой

Канальный

Транпортный

Физический

Представительский

Сеансовый

Прикладной

Сетевой

Канальный

Транпортный

ФизическийКаналсвязи

Рисунок 1.9. Модель взаимодействия вычислительных систем

В сетях наиболее распространенного стека протоколов TCP/IP протокол TCP является

протоколом транспортного, а протокол IP – протоколом сетевого уровня. Обеспечение

интерфейса к транспортному уровню в настоящее время берет на себя сетевая

компонента операционной системы, предоставляя обычно основанный на сокетах

Page 20: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 1. Введение в распределенные системы

15

интерфейс для верхних уровней. Сокеты обеспечивают примитивы низкого уровня для

непосредственного обмена потоком байт между двумя процессами. Стандартного

представительского или сеансового уровня в стеке протоколов TCP/IP нет, иногда к

ним относят защищенные протоколы SSL/TLS.

Использование протокола TCP/IP посредством сокетов предоставляет стандартный,

межплатформенный, но низкоуровневый сервис для обмена данными между

компонентами. Для выполнения сформулированных выше требований к

распределенным системам функции сеансового и представительского уровня должна

взять на себя некоторая промежуточная среда (middleware), называемая так же

промежуточным программным обеспечением (рис. 1.9). Такая среда должна помочь

создать разработчиками открытые, масштабируемые и устойчивые распределенные

системы. Для достижения этой цели промежуточная среда должна обеспечить сервисы

для взаимодействия компонент распределенной системы. К таким сервисам относятся:

– обеспечение единого и независимого от операционной системы механизма

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

компонент;

– обеспечение безопасности распределенной системы: аутентификация и

авторизация всех пользователей сервисов компоненты и защита передаваемой

между компонентами информации от искажения и чтения третьими сторонами;

– обеспечение целостности данных: управление транзакциями, распределенными

между удаленными компонентами системами;

– балансировка нагрузки на серверы с программными компонентами;

– обнаружение удаленных компонент.

В пределах одной распределенной системы может использоваться несколько видов

промежуточных сред (рис. 1.10). При хорошем подходе к проектированию системы

каждая распределенная ее компонента предоставляет свои сервисы посредством

единственной промежуточной среды, и использует сервисы других компонент

посредством так же единственной промежуточной среды, однако эти среды могут быть

различными.

Page 21: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 1. Введение в распределенные системы

16

Промежуточнаясреда 4

Промежуточнаясреда 1

Промежуточнаясреда 1

Промежуточнаясреда 3

Промежуточнаясреда 2

ИП

ЛП

Пользователи

ИП ИП

ЛП ЛП

ДДДД

Пользователи

ИП

ЛП

БД БД

Рисунок 1.10. Гетерогенная распределенная система

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

сред, можно называть гетерогенной, в противоположность гомогенной, использующей

единственную промежуточную среду. Поскольку одна и та же промежуточная среда

может быть реализована на различных аппаратных платформах и операционных

системах, то оба класса распределенных систем могут включать в себя компьютеры под

управлением как одной, так и различных операционных систем.

В настоящий момент нет универсально применимой промежуточной среды, хотя как

будет показано в курсе, есть определенное движение в этом направлении. Основной

причиной отсутствия такой среды являются отчасти противоречивые требования к

распределенным системам, а так же различающийся характер сетевых соединений

между компонентами системы: например взаимодействие компонент внутри одного

предприятия, вероятно, может быть построено иначе, чем взаимодействие компонент

двух различных предприятий, не полностью доверяющих друг другу.

Взаимодействие программных компонент в пределах одного и того же компьютера

также происходит при помощи промежуточной среды, что при использовании

некоторых промежуточных сред может быть как неудобно, так и неэффективно. В

Page 22: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 1. Введение в распределенные системы

17

идеальном случае распределенная компонента должна быть реализована таким

образом, чтобы переход с одной промежуточной среды на другую происходил путем

изменения конфигурации программной компоненты, а не изменения исходного кода.

На практике данное требование, к сожалению, может быть трудно осуществимо,

однако необходимо хотя бы минимизировать возможные исправления программного

кода при возможной смене промежуточной среды.

Page 23: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

18

Тема 2. Взаимодействие компонент распределенной системы

Рассматриваются две модели взаимодействия распределенных компонент:

использование сообщений и удаленный вызов. Описываются различные подходы

использования удаленных объектов и удаленных событий. Рассматривается

понятие распределенной транзакции.

2.1. Модели взаимодействия компонент распределенной системы

Ключевым сервисом промежуточной среды для создания распределенных систем

является обеспечение обмена данными между компонентами распределенной системы.

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

компонент: обмен сообщениями между компонентами и вызов процедур или методов

объекта удаленной компоненты по аналогии с локальным вызовом процедуры.

Поскольку в настоящее время любое взаимодействие между удаленными

компонентами в конечном итоге основано на сокетах TCP/IP, первичным с точки

зрения промежуточной среды является низкоуровневый обмен сообщениями на основе

сетевых сокетов, сервис которых никак не определяет формат передаваемого

сообщения. На базе протоколов TCP или HTTP затем могут быть построены

прикладные протоколы обмена сообщений более высокого уровня абстракции для

реализации более сложного обмена сообщениями или удаленного вызова процедур.

Удаленный вызов является моделью, происходящей от языков программирования

высокого уровня, а не от реализации интерфейса транспортного уровня сетевых

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

на какой-либо системе передачи сообщений, включая как непосредственное

использование сокетов TCP/IP, так и основанные на нем другие промежуточные среды

для обмена сообщениями. Реализация высокоуровневых служб обмена сообщениями, в

свою очередь, может использовать удаленный вызов процедур, основанный на более

низкоуровневой передаче сообщений, использующей, например, непосредственно

сетевые сокеты. Таким образом, одна промежуточная среда может использовать для

своего функционирования сервисы другой промежуточной среды, аналогично тому, как

Page 24: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

19

один протокол транспортного или сетевого уровня может работать поверх другого

протокола при туннелировании протоколов.

2.2. Обмен сообщениями

Существует два метода передачи сообщений от одной удаленной системы к другой –

непосредственный обмен сообщениями и использование очередей сообщений. В

первом случае передача происходит напрямую, и она возможна только в том случае,

если принимающая сторона готова принять сообщение в этот же момент времени. Во

втором случае используется посредник – менеджер очередей сообщений. Компонента

посылает сообщение в одну из очередей менеджера, после чего она может продолжить

свою работу. В дальнейшем получающая сторона извлечет сообщение из очереди

менеджера и приступит к его обработке.

Простейшей реализацией непосредственного обмена сообщениями является

использование транспортного уровня сети через интерфейс сокетов, минуя какое-либо

промежуточное программное обеспечение. Однако такой способ взаимодействия

обычно не применяется в системах автоматизации предприятия, поскольку в этом

случае реализация всех функций промежуточной среды ложится на разработчиков

приложения. При таком подходе сложно получить расширяемую и надежную

распределенную систему, поэтому для разработки прикладных распределенных систем

обычно используются системы очередей сообщений.

Существует несколько разработок в области промежуточного программного

обеспечения, реализующие высокоуровневые сервисы для обмена сообщениями между

программными компонентами. К ним относятся, в частности, Microsoft Message

Queuing, IBM MQSeries и Sun Java System Message Queue. Такие системы дают

возможность приложениям использовать следующие базовые примитивы по

использованию очередей:

– добавить сообщение в очередь;

– взять первое сообщение из очереди, процесс блокируется до появления в

очереди хотя бы одного сообщения;

– проверить очередь на наличие сообщений;

Page 25: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

20

– установить обработчик, вызываемый при появлении сообщений в очереди.

Менеджер очереди сообщений в таких системах может находиться на компьютере,

отличном от компьютеров с участвующими в обмене компонентами. В этом случае

сообщение первоначально помещается в исходящую очередь на компьютере с

посылающей сообщения компонентой, а затем пересылается менеджеру требуемой.

Для создания крупных систем обмена сообщениями может использоваться

маршрутизация сообщений, при которой сообщения не передаются напрямую

менеджеру, поддерживающему очередь, а проходят через ряд промежуточных

менеджеров очередей сообщений (рис. 2.1).

СерверКлиент

Приложение

Промежуточнаясреда

Исходящаяочередь

Менеджерыочередейсообщений

Промежуточнаясреда

Приложение

Промежуточнаясреда

Очередь Очередь

Рисунок 2.1. Системы очередей сообщений

Использование очередей сообщений ориентировано на асинхронный обмен данными.

Основные достоинства таких систем:

– время функционирования сервера может быть не связано со временем работы

клиентов;

– независимость промежуточной среды от средства разработки компонент и

используемого языка программирования;

– считывать и обрабатывать заявки из очереди могут несколько независимых

компонент, что дает возможность достаточно просто создавать устойчивые и

масштабируемые системы.

Недостатки систем очередей сообщений являются продолжением их достоинств:

– необходимость явного использования очередей распределенным приложением;

Page 26: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

21

– сложность реализации синхронного обмена;

– определенные накладные расходы на использование менеджеров очередей;

– сложность получение ответа: передача ответа может потребовать отдельной

очереди на каждый компонент, посылающий заявки.

2.3. Дальний вызов процедур

Идея удаленного вызова процедур (remote procedure call, RPC) появилась в середине

80-х годов и заключалась в том, что при помощи промежуточного программного

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

функцию на локальном компьютере. Чтобы удаленный вызов происходил прозрачно с

точки зрения вызывающего приложения, промежуточная среда должна предоставить

процедуру-заглушку (stub), которая будет вызываться клиентским приложением. После

вызова процедуры-заглушки промежуточная среда преобразует переданные ей

аргументы в вид, пригодный для передачи по транспортному протоколу, и передает их

на удаленный компьютер с вызываемой функцией. На удаленном компьютере

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

передаются вызываемой функции (рис. 2.2). Аналогичным образом на клиентскую

машину передается результат выполнения вызванной функции.

Клиентский процесс

Клиент

Заглушка клиента

Вызывающая функция

Каналпередачи данных

Сервер

Серверный процесс

Заглушка сервера

Вызываемая функция

Промежуточнаясреда

Промежуточнаясреда

Сетевые служба ОС Сетевые служба ОС

Рисунок 2.2. Удаленный вызов процедур

Page 27: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

22

Существует три возможных варианта удаленного вызова процедур.

1. Синхронный вызов: клиент ожидает завершения процедуры сервером и при

необходимости получает от него результат выполнения удаленной функции;

2. Однонаправленный асинхронный вызов: клиент продолжает свое выполнение,

получение ответа от сервера либо отсутствует, либо его реализация возложена на

разработчика (например, через функцию клиента, удалено вызываемую сервером).

Асинхронный вызов: клиент продолжает свое выполнение, при завершении сервером

выполнения процедуры он получает уведомление и результат ее выполнения, например

через callback-функцию, вызываемую промежуточной средой при получении

результата от сервера.

Процесс преобразования параметров для передачи их между процессами (или

доменами приложения в случае использования .NET) при удаленном вызове называется

маршализацией (marshaling). Преобразование экземпляра какого-либо типа данных в

пригодный для передачи за границы вызывающего процесса набор байт называется

сериализацией. Десериализация – процедура, обратная сериализации – заключается в

создании копии сериализованного объекта на основе полученного набора байт. Такой

подход к передаче объекта между процессами путем создания его копий называется

маршализацией по значению (marshal by value), в отличие от рассматриваемой в

следующем разделе маршализации по ссылке.

Процесс сериализации должен быть определен для всех типов данных, передаваемых

при удаленном вызове. К ним относятся параметры вызываемой функции и

возвращаемый функцией результат. В случае передачи параметров по ссылке

сериализации подлежат ссылаемые объекты, поскольку для самих указателей

сериализация не может быть применена. Это затрудняет использование механизма

удаленного вызова в языках, поддерживающих указатели на объекты неизвестного

типа.

Page 28: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

23

2.4. Использование удаленных объектов

В связи с переходом разработчиков прикладных программ от структурной парадигмы к

объектной появилась необходимость в использовании удаленных объектов (remote

method invocation, RMI). Удаленный объект представляет собой некоторые данные,

совокупность которых определяет его состояние. Это состояние можно изменять путем

вызова его методов. Обычно возможен прямой доступ к данным удаленного объекта,

при этом происходит неявный удаленный вызов, необходимый для передачи значения

поля данных объекта между процессами. Методы и поля объекта, которые могут

использоваться через удаленные вызовы, доступны через некоторый внешний

интерфейс класса объекта. Внешний интерфейс компоненты распределенной системы

в таких системах обычно совпадает с внешним интерфейсом одного из входящих в

компоненту классов.

В момент, когда клиент начинает использовать удаленный объект, на стороне клиента

создается клиентская заглушка, называемая посредником (proxy). Посредник реализует

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

посредника, который маршализирует их параметры для передачи по сети, и передает их

по сети серверу. Промежуточная среда на стороне сервера десериализует параметры и

передает их заглушке на стороне сервера, которую называют каркасом (skeleton) или,

как и в удаленном вызове процедур, заглушкой (рис. 2.3). Каркас связывается с

некоторым экземпляром удаленного объекта. Это может быть как вновь созданный, так

и существующий экземпляр объекта, в зависимости от применяемой модели

использования удаленных объектов, которые будут рассмотрены ниже.

Весь описанный процесс называется маршализацией удаленного объекта по ссылке

(marshal by reference). В отличие от маршализации по значению, экземпляр объекта

находится в процессе сервера и не покидает его, а для доступа к объекту клиенты

используют посредников. При маршализации же по значению само значение объекта

сериализуется в набор байт для его передачи между процессами, после чего следует

создание его копии в другом процессе.

Page 29: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

24

Клиентский процесс

Клиент

Посредник

Ccылка на посредника

Каналпередачи данных

Сервер

Серверный процесс

Каркас

Объект

Промежуточнаясреда

Промежуточнаясреда

Сетевые служба ОС Сетевые служба ОС

Интерфейс удаленногообъекта

Интерфейс удаленногообъекта

Состояние объекта

Рисунок 2.3. Использование удаленных объектов

Как и удаленный вызов процедур, вызов метода удаленного объекта может быть как

синхронным, так и асинхронным. Существует две особенности при использовании

удаленных объектов, не встречавшихся в удаленном вызове процедур. Во-первых, если

на момент формирования концепции удаленного вызова процедур исключения

(exceptions) еще не поддерживались распространенными языками и не использовались,

то в дальнейшем они стали методом информирования вызывающей стороны о

встреченных вызываемой стороной проблемах. Таким образом, в системах,

использующих удаленные объекты, сериализации подвержены как параметры метода и

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

Во-вторых, в качестве параметра или результата методов могут тоже передаваться

ссылки на удаленный объект (рис 2.4), если вызывающий удаленный метод клиент

также может является сервером RMI.

Page 30: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

25

Клиентский процесс

Посредник

Серверный процесс

Каркас

Маршализируемыйпо ссылке объект

Промежуточная среда

Маршализируемыйпо ссылке объект

ссылка Каркас Посредник

ссылка

Рисунок 2.4. Передача удаленному методу ссылки на объект,

маршализируемый по ссылке

При использовании удаленных объектов проблемными являются вопросы о времени их

жизни:

– в какой момент времени создается экземпляр удаленного объекта;

– в течение какого промежутка времени он существует.

Для описания жизненного цикла в системах с удаленными объектами используются два

дополнительных понятия:

– активация объекта: процесс перевода созданного объекта в состояние

обслуживания удаленного вызова, то есть связывания его с каркасом и

посредником.

– деактивация объекта: процесс перевода объекта в неиспользуемое состояние.

Выделяют три модели использования удаленных объектов – модель единственного

вызова (singlecall), модель единственного экземпляра (singleton), а так же модель

активации объектов по запросу клиента (client activation). Первых две модели так же

иногда называют моделями серверной активации (server activation), хотя, строго говоря,

активация всегда происходит на сервере после какого-либо запроса от клиента.

Page 31: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

26

Модель единственного вызова

При использовании данной модели объект активируется на время единственного

удаленного вызова. В наиболее простом случае для каждого вызова удаленного метода

объекта клиентом на сервере создается и активируется новый экземпляр объекта,

который деактивируется и затем удаляется сразу после завершения удаленного вызова

метода объекта. Таким образом, удаленные вызовы разных клиентов изолированы друг

от друга. Благодаря удалению объектов после вызова достигается экономное

расходование ресурсов памяти, но могут тратиться значительные ресурсы процессора

на постоянное создание и удаление объектов. Посредник на клиенте и заглушка на

сервер существуют до уничтожения посредника объекта (рис. 2.5).

Клиент

Сервер

Серверный процесс

Клиентский процесс Объект

Промежуточнаясреда

Промежуточнаясреда

Объект

Вызов метода

Вызов метода В

ызов метода

Вызов метода

Рисунок 2.5. Режим единственного вызова удаленного метода

Данный метод использования удаленных объектов можно рассматривать как

некоторый вариант удаленного вызова процедур, поскольку объект не сохраняет свое

состояние между вызовами. Тем не менее, сервер использует свои ресурсы на

поддержание каркаса и канала между посредником и заглушкой.

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

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

позволяющий поддерживать некоторое количество уже созданных, но еще не

Page 32: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

27

активированных объектов, которые используются для обработки удаленных вызовов.

Такой набор объектов, ожидающих своей активации, называется пулом объектов (object

pooling). По завершении удаленного вызова объекты деактивируются и могут либо

быть помещены в пул и использованы повторно в дальнейшем, либо удаляются, если

размер пула достиг некоторого максимального значения. Такая технология позволяет

достичь баланса между скоростью обработки запроса и объемом используемых

ресурсов сервера. Как видно из описания, в системах с пулом объектов активация не

всегда следует непосредственно после создания объекта, а удаление не всегда следует

сразу за деактивацией.

Отличительной особенностью метода одного вызова являются наименьшие затраты на

организацию системы балансировки нагрузки и ее наибольшая эффективность,

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

удаленного метода.

Модель единственного экземпляра

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

не более чем в одном экземпляре. Созданный объект существует, пока есть хоть один

использующий его клиент (рис. 2.6).

Клиент Сервер

Серверный процессКлиентскийпроцесс

Объект

Промежуточнаясреда

Промежуточнаясреда

Вызов метода

Вызов метода В

ызовы

методов

Клиент

Клиентскийпроцесс

Промежуточнаясреда

Вызов метода

Рисунок 2.6. Использование удаленных объектов в режиме

единственного экземпляра.

Page 33: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

28

При использовании модели единственного объекта вызовы различных клиентов

работают с одним и тем же экземпляром удаленного объекта. Поскольку вызовы

клиентов не изолированы друг от друга, то используемый объект не должен иметь

какого-либо внутреннего состояния. Модель единственного объекта позволяет

получить наиболее высокую производительность, поскольку объекты не создаются и не

активируются сервером при каждом вызове метода объекта.

Активация по запросу клиента

При каждом создании клиентом ссылки на удаленный объект (точнее, на посредника)

на сервере создается новый объект, который существует, пока клиент не удалит ссылку

на посредника. При таком методе использования вызовы различных клиентов

изолированы друг от друга, и каждый объект сохраняет свое состояние между

вызовами, что приводит к наименее рациональному использованию ресурсов памяти

сервера (рис. 2.7).

КлиентСервер

Серверный процессКлиентский процесс Объект

Промежуточная среда Промежуточная среда

Объект

Ссылка

Ссылка С

сылка

Ссылка

Рисунок 2.7. Объекты, активируемые клиентом.

Состояние компоненты распределенной системы

Программные компоненты с точки зрения пользователей своих сервисов можно

разделить на две категории:

– компоненты без сохраняемого между удаленными вызовами своих методов

внутреннего состояния (stateless components);

Page 34: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

29

– компоненты с внутренним состоянием, сохраняемым между удаленными

вызовами своих методов (statefull components).

Под состоянием в данном случае понимается совокупность значений полей

реализующих компоненту объектов, хранящихся в памяти сервера. Если компонента в

ходе своей работы сохраняет какие-либо данные во внешнем хранилище, например в

базе данных или очереди сообщений, это обычно не рассматривается как ее внутреннее

состояние.

Модель единственного вызова не сохраняет состояния удаленного объекта между

вызовами его методов, в силу чего данная модель может использоваться только с

распределенными компонентами без внутреннего состояния. Модель одного

экземпляра может быть использована для вызова компонент с внутренним состоянием,

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

клиентов в произвольном порядке. Модель активации по запросу клиента может быть

использована с любыми компонентами, но для компонент без внутреннего состояния

такой подход обычно ведет к непроизводительному расходу памяти при некотором

выигрыше в затратах времени процессора по сравнению с моделью одного вызова.

Компоненты без сохранения внутреннего состояния, используемые вместе с моделью

единственного вызова с пулом объектов, имеют наибольшие возможности

масштабирования системы при оптимальном балансе между затратами памяти и

нагрузкой на процессор.

Использование свойств удаленных объектов

Рассмотрим следующий фрагмент кода, заполняющий свойства удаленного объекта

информацией о человеке и вызывающий некий удаленный метод для ее обработки.

processing.FirstName = "Иван"; processing.SecondName = "Иванов"; processing.ThirdName = "Иванович"; processing.City = "Москва"; result = processing.Run();

Page 35: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

30

Если processing – локальный объект, то в методе Run() он будет иметь доступ к

установленным здесь значениям своих методов. Предположим, что processing –

удаленный объект. В этом случае возможны, например, следующие варианты.

1. Используется модель единственного вызова, причем установка свойства объекта

рассматривается как вызов метода установки свойства (set в C#). В этом случае к

моменту вызова метода Run состояние полей объекта не задано (при отсутствии пула

объектов) или неопределенно (при использовании пула объектов).

2. Используется модель единственного вызова, но установка свойств объекта не

рассматривается как удаленный вызов. Например, их значения сохраняются на стороне

клиента и передаются на сервер в момент вызова метода, где на основе этих значений

заполняются поля объекта. В этом случае результат будет корректен.

3. Используется модель активации по запросу клиента, или такой вариант модели

единственного вызова, в котором присваивание свойств объекта приводит к передаче

данных на сервер без последующей деактивации объекта (по существу такая модель

уже не является моделью одного вызова). В этом случае результат так же будет

корректен, но присваивание свойств объекту приводит к непроизводительным

удаленным вызовам,

4. Используется модель одного экземпляра. В этом случае результат будет

некорректным, поскольку поля объекта будут заполняться параллельно разными

клиентами.

Таким образом, один и тот же код может давать разные результаты в зависимости от

модели использования удаленной компоненты, причем только в одном из примеров

был получен корректный результат при минимальных накладных расходах. Можно

сделать заключение, что использование в программе свойств удаленного объекта

приводит к зависимости ее результата от используемой промежуточной среды и ее

конфигурации. Наилучшим вариантом решения этой проблемы представляется

отсутствие публичных свойств у удаленного объекта. В таком случае использование

удаленного объекта может не отличаться от вызова метода локального объекта. Однако

Page 36: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

31

применение такого подхода «в лоб» (так называемый chunky design) может привести к

плохо читаемому коду, например следующему.

result = processing.Run("Иванов", "Иван", "Иванович", "Москва");

В отличие от предыдущего фрагмента, это код не является самодокументирующимся, и

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

метода. В данном примере так же наблюдается смешивание двух сущностей: объекта,

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

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

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

параметра удаленного вызова метода.

person.FirstName = "Иван"; person.SecondName = "Иванов"; person.ThirdName = "Иванович"; person.City = "Москва"; result = processing.Run(person);

Для многих задач представляет интерес вопрос модификации поведения удаленного

объекта путем добавления некоторого дополнительного кода. Рассмотрим следующий

фрагмент кода, вызывающий некий математический метод для заданной функции и

заданного отрезка значений аргумента. Было бы удобно отделить реализацию самого

математического метода от применяемой функции.

task.Epsilon = 1e-5: task.X1 = -1; task.X2 = 1; task.MaximumSteps = 20; task.Function += X2Function; result = processing.Run(processingArguments); ... double X2Function(double x) { return x*x; }

Если processing – это удаленный объект, то данный код, вероятно, приведет к

многочисленным удаленным вызовам функции X2Function. В этом случае приведенный

код имеет два главных недостатка – во-первых, клиент для этого должен так же

поддерживать удаленные вызовы, во-вторых, он неэффективен, если удаленный метод

постоянно вызывает находящуюся на стороне клиента функцию.

Page 37: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

32

Для решения возникшей проблемы в общем случае требуется передать тем или иным

способом кода функции (и, возможно, используемых ею данных) на сервер с

реализующим математический метод удаленным объектом. Такой подход неприменим

в общем случае по соображениям безопасности, но при необходимости можно,

например, при использовании .NET Framework передать клиенту сборку с требуемым

кодом как данные при удаленном вызове. В качестве возможной альтернативы для

математических задач может выступать и передача кода функции для дальнейшей

компиляции встроенным компилятором языка C#.

2.5. Распределенные события

При разработке программного обеспечения достаточно часто возникает потребность

получать извещения о каких-либо событиях, возникающих асинхронно, то есть в

некоторые произвольные моменты времени. В распределенных системах так же может

возникнуть необходимость использования таких извещений, получаемых от удаленной

системы. Можно выделить два подхода к обработке событий – тесно связанные и слабо

связанные события. При тесно связанном событии происходит прямое уведомление

одной стороны другой стороной. Хотя этот метод можно использовать, например,

вместе с однонаправленным асинхронным вызовом, ему свойственен ряд недостатков,

ограничивающих его применение в распределенных системах:

– обе компоненты системы должны выполняться одновременно;

– для уведомления нескольких компонент об одном событии уведомляющей

стороной должны использоваться механизмы для ведения списка получателей

событий;

– затруднена фильтрация или протоколирование событий.

Поэтому в распределенных системах так же применяются слабо связанные события,

когда источники события (издатели) не взаимодействуют напрямую с получателями

событий (подписчиками). Промежуточная среда в этом случае должна предоставить

сервис, позволяющий подписчику подписаться на какое-либо событие или отказаться

от подписки, а издателю – инициировать событие для рассылки подписчикам (рис 2.8).

Page 38: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

33

ПодписчиксобытияПодписчик

событияПодписчикисобытия

Событие ИздательУведомления

Промежуточнаясреда

Рисунок 2.8. Подписчики и издатели слабосвязанных событий

При использовании слабосвязанных событий подписчики, издатели и менеджер

событий могут располагаться на различных компьютерах. Само событие может быть

реализовано как, например, вызов менеджером событий некоторого

зарегистрированного метода удаленного объекта.

2.6. Распределенные транзакции

Транзакция – последовательность операций с какими-либо данными, которая либо

успешно выполняется полностью, либо не выполняется вообще. В случае

невозможности успешно выполнить все действия происходит возврат к

первоначальным значениям всех измененных в течение транзакции данных (откат

транзакции). Транзакция должна обладать следующими качествами.

– Атомарность. Транзакция выполняется по принципу «все или ничего».

– Согласованность. После успешного завершения или отката транзакции все

данные находятся в согласованном состоянии, их логическая целостность не

нарушена.

– Изоляция. Для объектов вне транзакции не видны промежуточные состояния,

которые могут принимать изменяемые в транзакции данные. С точки зрения

«внешних» объектов, до успешного завершения транзакции они должны иметь

то же состояние, в котором находились до ее начала.

– Постоянство. В случае успешности транзакции сделанные изменения должны

иметь постоянный характер (т.е. сохранены в энергонезависимой памяти).

Page 39: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

34

ИП

ЛП

Пользователь

ДДЛПДД

ЛПДД

БДБД

Участники распределенной транзакции

Рисунок 2.9. Распределенная транзакция

Транзакции являются основой приложений, работающих с базами данных, однако в

распределенной системе может быть недостаточно использования только транзакций

систем управления базами данных. Например, в распределенной системе в транзакции

может участвовать несколько распределенных компонент, работающих с несколькими

независимыми базами данных (рис. 2.9).

Распределенной называется транзакция, охватывающая операции нескольких

взаимодействующих компонент распределенной системы. Каждая из этих компонент

может работать с какими-либо СУБД или иными службами, например, использовать

очереди сообщений, или даже работать с файлами. При откате транзакции все эти

операции должны быть отменены. Для этого необходимо выполнение двух условий:

– промежуточная среда должна поддерживать управление распределенными

между несколькими компонентами транзакциями;

– компоненты распределенной системы не должны работать с какими-либо

службами или ресурсами, которые не могут участвовать в транзакции.

Распределенные транзакции являются важнейшим элементом поддержания

целостности данных в распределенной системе. Поэтому для более широкого их

применения промежуточная среда может содержать механизмы, которые при

необходимости (и определенных затратах времени на написание кода) позволят

использовать в распределенных транзакциях внешние службы, не поддерживающие

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

Page 40: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

35

(compensating resource manager). Компенсация в данном случае означает возврат

ресурса к первоначальному состоянию при откате транзакции.

В настоящее время происходит формирование и стандартизация еще одного понятия,

связанного с поддержкой целостности данных – хозяйственной деятельности (business

activity) применительно к распределенным системам. Деятельность обычно является

отражением некоторого реального процесса, например, покупки в магазине: от

оформления заказа до подтверждения доставки курьером. Деятельность может

включать в себя транзакции (оформление заказа покупателя, заказ товара у поставщика,

и так далее – до подтверждения доставки покупателем). В отличие от транзакции,

время жизни которой предполагается коротким, деятельность может длиться в течение

очень долгого времени (например, месяца). Деятельность может поддерживать отмену

сделанных изменений (например, оформление возврата товара поставщика при отказе

покупателя) путем использования компенсирующих задач.

2.7. Безопасность в распределенных системах

Для обеспечения безопасности распределенной системы промежуточная среда должна

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

безопасных систем.

1. Проверка подлинности пользователя сервисов компоненты распределенной

системы (аутентификация1). Проверка подлинности может быть односторонней, когда

только сервер убеждается в подлинности клиента, или двухсторонней, когда клиент так

же убеждается в подлинности сервера.

2. Ограничение доступа к сервисам компоненты в зависимости от результатов

аутентификации (авторизация). Для решения данной задачи промежуточная среда

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

(role-based security). Поскольку разработчики компоненты не могут обозначить уровни

1 Аутентификация является стандартным термином в русской литераторе, хотя данный термин является

«склейкой» английских authentication и identification.

Page 41: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

36

доступа через конкретных пользователей или групп пользователей системы, то они

должны использовать некоторые абстрактные роли, которые при развертывании

компоненты будут связаны администратором системы с учетными записями

пользователей системы.

3. Защита данных, передаваемых между компонентами системы, от просмотра и

изменения третьими сторонами. Для этого передаваемые между компонентами

сообщения должны подписываться электронной подписью и шифроваться как

клиентом, так и сервером.

Функции обеспечения безопасности могут обеспечиваться транспортным протоколом,

используемым промежуточной средой, самой средой, или ими обеими в совокупности.

2.8. Промежуточные среды в Microsoft .NET Framework

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

инкапсуляции удаленного взаимодействия, появилось в середине 90-х годов, когда

выяснилось, что для создания распределенных систем необходима некоторая

независящая от приложения и операционной среды «прослойка». Среда CLR так же

может рассматриваться как некоторая «промежуточная» среда для выполнения

программ на управляемом коде. Поэтому закономерно использовать .NET Framework в

качестве основы для создания распределенных приложений.

В настоящий момент в .NET Framework Class Library присутствует поддержка четырех

промежуточных сред для построения распределенных систем. Далее они перечислены в

порядке даты выпуска промежуточной среды.

– Среда Microsoft Message Queuing (MSMQ) поддерживает обмен сообщениями

между программными компонентами на основе очередей.

– Среда Microsoft Enterprise Services основана на разработанной ранее фирмой

Microsoft среде COM+, которая позволяет использовать удаленные объекты и

распределенные транзакции в локальной сети.

– Среда ASP .NET Web Services позволяет организовать удаленный вызов на

основе общепринятых стандартов, базирующихся на языке XML.

Page 42: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

37

– Среда .NET Remoting была разработана как универсальная промежуточная среда

для использования удаленных объектов.

В версии .NET Framework 3.0 предполагается ввести технологию WCF (Windows

Communication Foundation), объединяющую все упомянутые технологии построения

распределенных систем. Кроме указанных технологий, приложения на .NET Framework

могут использовать, например, удаленные вызовы на основе стандарта XML-RPC при

подключении дополнительных библиотек.

Page 43: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

38

Тема 3. Описание интерфейса программной компоненты

Рассматривается понятие интерфейса компоненты, языков описания интерфейса и

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

существующие спецификации данных языков.

3.1. Сервисы и интерфейс программной компоненты

Для работы с сервисами программной компоненты обращающийся к ним клиент

должен иметь полное представление об интерфейсе используемой компоненты.

Несмотря на значительные отличия модели передачи сообщений и модели удаленного

вызова, для них обеих интерфейс компоненты распределенной системы можно описать

как совокупность адресов и форматов сообщений ее сервисов. В роли сервиса,

предоставляемой программной компонентой, созданной с помощью .NET Framework,

выступает одно из следующих понятий:

– методы активируемого сервером объекта;

– активируемый клиентом объект вместе со своими полями, свойствами и

методами;

– очередь с сообщениями-запросами, которые считываются программной

компонентой.

Адрес сервиса зависит от промежуточной среды и является совокупностью сетевого

адреса компоненты и некоторого публичного имени сервиса. Сетевой адрес

программной компоненты основан на имени ее компьютера для систем удаленного

вызова или на адресе менеджера очереди для систем обмена сообщениями. Данный

адрес является адресом нижестоящего протокола, на котором основана данная

промежуточная среда. В роли него может выступать HTTP, TCP, NetBIOS, или

протокол нижестоящей промежуточной среды. Второй составляющей адреса сервиса

является идентификатор сервиса. В роли него может выступать некий идентификатор

активируемого класса для сред удаленного вызова или же имя очереди сообщений, из

которой сервис считывает сообщения-запросы. Хотя имя вызываемого метода часто

фактически описывается в самом сообщении, его следует рассматривать как составную

Page 44: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

39

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

различных методов одного и того же класса.

Если компонента системы передачи сообщений посылает сообщения-ответы клиенту,

то можно считать, что сервис такой компоненты имеет два адреса – один для очереди

запросов и второй для очереди ответов (имя очереди ответов может быть задано и в

сообщении-запросе).

Кроме информации о полном адресе сервиса, клиенту компоненты необходимо знать

формат сообщений, получаемых и возвращаемых сервисом. К первым относятся

сообщения с параметрами удаленного вызова и сообщения-запросы в очередях

сообщений, ко вторым – сообщения с результатом выполнения метода и

сообщения-ответы. К параметрам удаленного метода следует отнести и некоторый

идентификатор активированного объекта сервера для случая активации объектов по

запросу клиента. Можно постулировать, что каждому сервису компоненты должна

соответствовать единственная спецификация формата принимаемых им сообщений и

единственная спецификация принимаемых от него сообщений (в частном случае это

спецификация информирует об отсутствии ответа компоненты).

Важным различием систем обмена сообщениями от систем удаленного вызова является

отсутствие ограничений на формат сообщения. Таким образом, формально в них

существует возможность использования для описания формата сообщения, например,

контекстно-свободных формальных грамматик. Однако было бы естественным считать,

что формат сообщения должен быть эквивалентен описанию полей некоторого класса

CLI. Объект данного класса преобразуется в результате сериализации в передаваемое

сообщение.

Если и каждое сообщение в системах очередей сообщений, и параметры удаленного

вызова метода будут представлять собой единственный сериализованный объект

некоторого сложного типа данных, то различие между системами с активируемыми

сервером объектами и системами передачи сообщений становится минимальным.

Кроме того, ранее было показано, что единственный параметр удаленного вызова

является хорошим решением проблемы недоступности свойств активируемых сервером

Page 45: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

40

объектов. Поэтому существует рекомендация создавать удаленные методы с

единственным параметром сложного типа. Этот объект должен маршализироваться по

значению, как и все его поля и свойства.

Итого, каждый сервис программной компоненты характеризуется тремя сущностями:

– полным адресом сервиса;

– единственной спецификацией принимаемых сервисом сообщений (запросов);

– единственной спецификацией принимаемых от сервиса сообщений (ответов).

Совокупность спецификаций всех сервисов программной компоненты образует ее

интерфейс (рис. 3.1).

Спецификация интерфейса программной компоненты

Идентификаторсервиса

Форматвходящегосообщения

Форматисходящегосообщения

Идентификаторсервиса

Форматвходящегосообщения

Форматисходящегосообщения

Адрескомпоненты

Идентификаторсервиса

Форматвходящегосообщения

Форматисходящегосообщения

Спецификация сервиса

Рисунок 3.1. Интерфейс компоненты распределенной системы

Для полного формального описания взаимодействий двух компонент распределенной

системы необходимы в общем случае три языка:

– язык передаваемых в распределенной системе сообщений, обычно

описывающий результат сериализации объектов;

– язык описания спецификаций сообщений, определяющий корректные

сообщения для сервисов компоненты;

– язык описания интерфейса компоненты, определяющий набор ее сервисов.

Языки описания интерфейса и спецификаций сообщений часто представлены на

практике одним языком.

Поскольку сообщение обычно представлено результатом сериализации того или иного

класса, то одной из спецификаций сообщения можно считать совокупность

сериализуемых полей и свойств маршализируемого по значению объекта. Для систем

удаленного вызова спецификацией интерфейса может являться описание класса .NET.

Page 46: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

41

Таким образом, метаданные из сборок с описанием интерфейса или класса удаленного

объекта и классами параметров его методов полностью определяют интерфейс

программной компоненты, созданной при помощи .NET Framework. Однако такой

подход часто неудобен, поскольку не только уменьшает открытость системы,

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

создания средству разработки, но и требует предоставления в общем случае сборок с

классами компоненты клиенту. Поэтому существует потребность в общепринятых и

независимых от средств разработки программных компонент языках описания

интерфейса компоненты.

3.2. Язык XML и схемы XML

Язык XML (Extensible Markup Language) в настоящее время нашел множество

разнообразных применений и является основой для большого числа общепринятых

спецификаций. На рис. 3.2 представлены основные используемые в распределенных

системах спецификации, основанные на языке XML – XSD, SOAP, WSDL, – которые

будут рассмотрены далее.

XML: языкразметки

XSD: описаниесхемы документа

SOAP: спецификациясообщений

WSDL: спецификациясервиса

Рисунок 3.2. XML и основанные на нем спецификации

Язык XML представляет собой язык разметки текстового документа, представленного

совокупностью именованных, древовидно вложенных элементов. Каждый элемент

может иметь некоторое текстовое значение и набор атрибутов, имеющих имя и простое

Page 47: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

42

значение (строку). Язык XML является абстрактным языком разметки, не

определяющим как-либо смысл элементов документа. Документы XML достаточно

хорошо читаются как человеком, так и многочисленными программными

анализаторами. При естественном подходе к именам элементов и атрибутов он

является самодокументирующимся языком. Перед древовидной структурой элементов,

имеющих единственный корень, могут идти отдельные элементы с метаинформацией,

указывающий в частности кодировку документа и версию языка, как показано в

следующем примере.

<?xml version="1.0" encoding="utf-8"?> <GeomFigures> <Point X="2" Y="-1" /> <Line> <A X="-1" Y="-1" /> <B X="2" Y="2" /> </Line> </GeomFigures>

Основными недостатками XML с точки зрения обмена сообщениями является

неудобное, в силу его древовидной структуры, представление отношения «многие ко

многим», а также несколько большие затраты времени на передачу и разбор сообщений

на языке XML по сравнению с двоичным представлением аналогичных данных.

Поскольку свойственное XML открытое представление информации не всегда удобно с

точки зрения безопасности, то существует спецификации XML-DigitalSignature и

XML-Encrypton, предназначенные для передачи в XML конфиденциальной

информации. Первая из них позволяет добавить к XML-документу цифровую подпись,

вторая – зашифровать XML-документ или отдельные его элементы.

Для определения назначения элементов и атрибутов XML-документа введено понятие

пространства имен XML (XML namespace), которое должно иметь уникальный

идентификатор. Обычно пространство имен идентифицируется некоторым URI

(Uniform Resource Identifier), связанным с доменом организации, предложившее

пространство имен. По данному URI может располагаться некоторое описание

пространства имен, однако это не обязательно. Все используемые в XML-документе

пространства имен описываются в корневом элементе документа в атрибутах с именем

вида xmlns:schema_id. Таким образом схема связывается с некоторым коротким

Page 48: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

43

идентификатором schema_id, который затем используется как префикс атрибутов и

элементов. Например, в следующей строчке пространству имен

http://www.w3.org/2001/XMLSchema дается идентификатор xs, используемый в имени

элемента xs:schema.

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

Часть имени элемента после двоеточия является локальной, а часть перед двоеточием

должна быть связана со схемой в корневом атрибуте. Благодаря такой нотации разные

пространства имен могут иметь совпадающие имена элементов. Пространства имен

используются оперирующими с XML программами.

Одним из достоинств XML является наличия языков спецификаций, определяющих

правильный XML-документ. Первоначально эту функцию выполнял DTD (Document

Type Definition), однако в настоящий момент общепринятым стандартом является

спецификация схем XML (XML Schema Definition, XSD). XSD-документ является также

документом на языке XML, использующим пространство имен

http://www.w3.org/2001/XMLSchema. Файл с описанием схемы XML определяет:

– словарь документа (имена элементов и атрибутов);

– синтаксис корректного документа;

– сложные типы данных.

Элементарные типы данных описаны в самом пространстве имен

http://www.w3.org/2001/XMLSchema. Синтаксис схемы позволяет описать сложные типы

данных, включающие в себя поля простых типов, сложных типов, а также сложные

последовательности, каждый элемент которых может принадлежать к одному из

нескольких сложных или простых типов данных. Таким образом, синтаксис схемы

XML позволяет описать результат сериализации контейнера (списка или массива), в

котором могут храниться объекты нескольких известных классов. В состав .NET

Framework входит утилита xsd.exe, создающая схему по заданному классу, которая

будет подробно рассмотрена в следующей главе. Соответствие между классами .NET и

схемами не является взаимно однозначным. В частности, при использовании утилиты

xsd.exe любым видам списочных контейнеров соответствует одинаковая

последовательность в схемах XML.

Page 49: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

44

3.3. SOAP: язык сообщений распределенной системы

Стандартизация описания языка XML дала широкие возможности для построения на

его основе языков описания сообщений, передаваемых между программными

компонентами, и языков описания сервисов программной компоненты. В конце 90-х

годов началась разработка двух спецификаций для построения распределенных

гетерогенных систем – SOAP и XML-RPC. Спецификация XML-RPC поддерживается в

настоящий момент большим числом языков, но имеет меньше возможностей и не

поддерживается стандартной библиотекой .NET Framework.

Поскольку в момент разработки данных спецификаций протокол HTTP был как

наиболее распространенным, так и повсеместно разрешенным в межсетевых экранах

протоколом, то он был выбран в качестве стандартного транспортного протокола для

создания гетерогенных промежуточных сред. В силу этого, хотя спецификация SOAP

не привязана жестко к какому-либо транспортному протоколу, использующая SOAP и

WSDL промежуточная среда получила названия веб-служб (web services). Она

использует два дополнительных языка – язык описания сообщения SOAP

(пространство имен SOAP версии 1.1 – http://schemas.xmlsoap.org/soap/envelope/, версии

1.2 – http://schemas.xmlsoap.org/wsdl/soap12/) и язык описания сервисов и интерфейсов

WSDL (пространство имен http://schemas.xmlsoap.org/wsdl/).

Рекомендация SOAP изначально разрабатывалась как спецификация для удаленного

вызова методов и расшифровывалась как Simple Object Access Protocol. Сообщение

SOAP представляет собой XML-документ, называемый конвертом или пакетом

(envelope), содержащий заголовки с метаинформацией в элементе soap:Header и тело

сообщения в элементе soap:Body. В заголовках пакета содержится дополнительная

информация, которая может использоваться промежуточной средой. Благодаря тому,

что основной стандарт не ограничивает содержание заголовков, SOAP является

расширяемой спецификацией, и в настоящее время все еще идет процесс

стандартизации ее расширений.

Что касается представления тела сообщения, то в силу различных причин в настоящий

момент существует два различных способа представления информации в теле пакета

Page 50: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

45

SOAP – кодирование SOAP-RPC (в двух вариантах) и кодирование SOAP-Document.

Кодирование SOAP-RPC предназначено исключительно для передачи параметров

удаленного вызова и определяет сообщение как имя метода и список параметров. При

использовании кодирования SOAP-Document, которое является фактическим

стандартом в настоящий момент, сообщение представляет собой XML-документ со

схемой и пространством имен, заданными в описании сервиса на языке WSDL. Хотя

обычно сообщение и состоит из имени метода удаленного объекта и списка его

параметров, но сама спецификация кодирования не фиксирует как-либо его

содержание.

В качестве примера рассмотрим простейшие сообщения SOAP версии 1.2 для сервиса,

складывающего два числа. Сообщение-запрос использует пространство имен

http://summa.test/webservices, которое описано в интерфейсе компоненты, что будет

показано далее. В элементе message содержатся сами складываемые числа.

<?xml version="1.0" encoding="utf-8"?> <soap12:Envelope xmlns:soap12="http://www.w3.org/2003/05/soap-envelope"> <soap12:Body> <Add xmlns="http://summa.test/webservices"> <message> <a>1</a> <b>2</b> </message> </Add> </soap12:Body> </soap12:Envelope>

Сообщение с ответов программной компоненты.

<?xml version="1.0" encoding="utf-8"?> <soap12:Envelope xmlns:soap12="http://www.w3.org/2003/05/soap-envelope"> <soap12:Body> <AddResponse xmlns="http://summa.test/webservices"> <AddResult>3</AddResult> </AddResponse> </soap12:Body> </soap12:Envelope>

3.4. WSDL: описание интерфейса программной компоненты

Для описания интерфейса программной компоненты, включая спецификацию

корректных сообщений, был разработан язык WSDL (Web Service Definition Language).

Описание на языке WSDL включает в себя следующие семь составляющих (рис. 3.3).

Page 51: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

46

Описаниеабстрактныхопераций

Описаниетипов данных Типы данных

Сообщения

ОперацииТипы портовПривязки

Описаниеинтерфейса Порт

Служба

Рисунок 3.3. Составные части WSDL-документа

1. Описание типов передаваемых данных. При использовании кодирования

SOAP-Document оно состоит из схемы XML, определяющей корректные сообщения,

получаемые программной компонентой в теле пакета SOAP.

2. Описание входящих и исходящих сообщений, которые связываются с описанными

типами данных.

3. Описание операций (сервисов программной компоненты), с каждой из которых

связывается входящее и исходящее сообщение.

4. Описание типов портов (идентификаторов программных компонент), с каждым из

которых связывается некоторый набор операций.

5. Описание привязок (binding), связывающие типы портов и их сообщений с

определенным типом кодирования тела пакета, а также с версией протокола SOAP.

6. Описание портов, связывающие типы портов и соответствующие им привязки с

конкретными URL.

7. Общее описание службы (интерфейса программной компоненты) как совокупности

портов.

Далее рассмотрено описание на языке WSDL интерфейса компоненты, которое

содержит два сервиса – сложение двух чисел и сложение последовательности чисел. В

Page 52: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

47

корневом элементе указаны все используемые пространства имен, включая

пространство протокола SOAP 1.2 – http://schemas.xmlsoap.org/wsdl/soap12/.

<?xml version="1.0" encoding="utf-8"?> <wsdl:definitions xmlns:tns="http://summa.test/webservices" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" targetNamespace="http://summa.test/webservices" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">

В элементе wsdl:types описываются все типы данных. Тип Add будет связан со

входящим сообщением сервиса сложения двух чисел, а тип AddResponse – с его

исходящим сообщением.

<wsdl:types> <s:schema elementFormDefault="qualified" targetNamespace="http://summa.test/webservices"> <s:element name="Add"> <s:complexType> <s:sequence> <s:element minOccurs="1" maxOccurs="1" name="message" type="tns:AddMessage" /> </s:sequence> </s:complexType> </s:element> <s:complexType name="AddMessage"> <s:sequence> <s:element minOccurs="1" maxOccurs="1" name="a" type="s:int" /> <s:element minOccurs="1" maxOccurs="1" name="b" type="s:int" /> </s:sequence> </s:complexType> <s:element name="AddResponse"> <s:complexType> <s:sequence> <s:element minOccurs="1" maxOccurs="1" name="AddResult" type="s:int" /> </s:sequence> </s:complexType> </s:element>

Типы SumList и SumListResponse предназначены для сообщений сервиса сложения

списка чисел.

<s:element name="SumList"> <s:complexType> <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="list" type="tns:ArrayOfInt" /> </s:sequence> </s:complexType> </s:element>

Page 53: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

48

<s:complexType name="ArrayOfInt"> <s:sequence> <s:element minOccurs="0" maxOccurs="unbounded" name="int" type="s:int" /> </s:sequence> </s:complexType> <s:element name="SumListResponse"> <s:complexType> <s:sequence> <s:element minOccurs="1" maxOccurs="1" name="SumListResult" type="s:int" /> </s:sequence> </s:complexType> </s:element> </s:schema> </wsdl:types>

В элементах wsdl:message типы данных связываются с идентификаторами сообщений.

<wsdl:message name="AddSoapIn"> <wsdl:part name="parameters" element="tns:Add" /> </wsdl:message> <wsdl:message name="AddSoapOut"> <wsdl:part name="parameters" element="tns:AddResponse" /> </wsdl:message> <wsdl:message name="SumListSoapIn"> <wsdl:part name="parameters" element="tns:SumList" /> </wsdl:message> <wsdl:message name="SumListSoapOut"> <wsdl:part name="parameters" element="tns:SumListResponse" /> </wsdl:message>

В элементе wsdl:portType описываются абстрактные операции и используемые ими

сообщения.

<wsdl:portType name="MathServiceSoap"> <wsdl:operation name="Add"> <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">

Операция Add складывает два числа </wsdl:documentation>

<wsdl:input message="tns:AddSoapIn" /> <wsdl:output message="tns:AddSoapOut" /> </wsdl:operation> <wsdl:operation name="SumList"> <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"> Операция SumList складывает несколько чисел </wsdl:documentation> <wsdl:input message="tns:SumListSoapIn" /> <wsdl:output message="tns:SumListSoapOut" /> </wsdl:operation> </wsdl:portType>

В элементе wsdl:binding операции связываются с транспортным протоколом (HTTP),

версией протокола SOAP (1.2) и типом кодирования тела пакета (SOAP-Document).

Page 54: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

49

<wsdl:binding name="MathServiceSoap12" type="tns:MathServiceSoap"> <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" /> <wsdl:operation name="Add"> <soap12:operation soapAction="http://summa.test/webservices/Add" style="document" /> <wsdl:input> <soap12:body use="literal" /> </wsdl:input> <wsdl:output> <soap12:body use="literal" /> </wsdl:output> </wsdl:operation> <wsdl:operation name="SumList"> <soap12:operation soapAction="http://summa.test/webservices/SumList" style="document" /> <wsdl:input> <soap12:body use="literal" /> </wsdl:input> <wsdl:output> <soap12:body use="literal" /> </wsdl:output> </wsdl:operation> </wsdl:binding>

В элементе wsdl:service интерфейс программной компоненты связывается с типом

порта, с некоторой привязкой, а также с конкретным URL, используемым в

дальнейшем для вызова веб-службы.

<wsdl:service name="MathService"> <wsdl:port name="MathServiceSoap12" binding="tns:MathServiceSoap12"> <soap12:address location="http://summa.test/webservices/summa.asmx" /> </wsdl:port> </wsdl:service> </wsdl:definitions>

Как видно из примера, структура документа на языке WSDL является достаточно

сложной, однако благодаря ей одни и те же абстрактные операции могут быть связаны

с различными способами передачи информации, включая как разные транспортные

протоколы, так и различные версии спецификации SOAP. Для реализации удаленного

вызова на основе данной спецификации необходимы средства как для создания

WSDL-документа по описанию используемого удаленно класса, так и средство

создания по известному WSDL-документу посредника удаленного вызова.

3.5. Выводы по описанию интерфейса компоненты

Для создания открытой распределенной системы необходимо использование

общепринятых языков описания интерфейса программной компоненты. В настоящий

момент существует ряд апробированных на практике стандартов для передачи данных

Page 55: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 3. Описание интерфейса программной компоненты

50

в гетерогенных распределенных системах: XML, XSD, SOAP и WSDL. Их

использование позволяет создавать системы, не привязанные жестко к какому-либо

средству разработки программ или транспортному протоколу. Однако открытый

характер спецификации SOAP допускает как реализацию использующей ее

промежуточной средой некоторой дополнительной функциональности, так и принятие

все новых стандартов и расширений, использующих заголовки SOAP. Это может

привести к определенным сложностям при взаимодействии основанных на WSDL и

SOAP программных компонент различных разработчиков.

Page 56: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 4. Сериализация объектов. Способы сериализации в .NET Framework

51

Тема 4. Сериализация объектов. Способы сериализации в .NET Framework

Рассматривается понятие графа объектов и проблема его передачи между двумя

компьютерами. Описывается проблема сериализация графа объектов, приводится

классификация методов сериализации. Приводятся описание различных методов

сериализации, которые используются в .NET Framework, и их особенностей.

4.1. Сериализация графа объектов

В отличие от приложений на неуправляемом коде, приложения .NET Framework не

обязательно выполняются в виде отдельных процессов, а могут существовать в

пределах одного процесса операционной системы в своих собственных областях,

называемых доменами приложения. Такие области можно рассматривать как

некоторые логические процессы виртуальной машины CLR. Использование

управляемого кода позволяет при этом гарантировать изоляцию приложений в

пределах своих областей. При передаче между доменами приложений некоторого

объекта для его класса должна быть определена процедура сериализации, которая

позволяет сохранить состояние объекта в некотором внешнем хранилище (например, в

файле, или в сообщении транспортного протокола) при помощи потоков ввода-вывода,

и процедура десериализации, создающая копию объекта по сохраненному состоянию

(рис 4.1). Следует отметить, что в общем случае это могут быть объекты разных

классов, и даже созданные в разных системах разработки приложений.

Домен приложения 1

Сериализация

Объект

Домен приложения 2

Десерилизация

Копия объекта

Поток вывода Поток вводаХранилище

Рисунок 4.1. Сериализация и десериализация объекта

Page 57: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 4. Сериализация объектов. Способы сериализации в .NET Framework

52

Задача сериализации объекта, включающего только поля из элементарных

типов-значений (наследников класса System.ValueType) и строк, не представляет

принципиальных трудностей. Для такого объекта в ходе сериализации в поток

записываются сами значения всех полей объекта. Однако в общем случае объект

содержит ссылки на другие объекты, которые, в свою очередь, могут ссылаться друг на

друга, образуя так называемый граф объектов (object graph). Сами ссылки не могут

быть сохранены в потоке ввода-вывода, поэтому основной вопрос сериализации – это

способ замены ссылок.

Граф объектов – ориентированный граф G = < V, E > , в котором вершины – это

объекты (множество V), а ребра направлены от объектов, содержащие ссылки, на

ссылаемые объекты (рис 4.2).

public class SampleClass { public SampleClass fieldA = null; public SampleClass fieldB = null; } ... SampleClass root = new SampleClass(); SampleClass child1 = new SampleClass(); SampleClass child2 = new SampleClass(); root.fieldA = child1; root.fieldB = child2; child1.fieldA = child2; ...

root child 1

child 2

Рисунок 4.2. Граф объектов

Наличие ссылки на объект B в объекте A есть принадлежность пары < A, B > множеству

E. При рассмотрении графа все поля объектов, относящиеся к простым

типам-значениям и строкам, можно исключить из рассмотрения в силу тривиальности

их сериализации. Хотя формально строки являются ссылочными типами, особенность

реализации строк в CLI не дает возможность изменить уже созданную строку. Эта

Page 58: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 4. Сериализация объектов. Способы сериализации в .NET Framework

53

особенность позволяет тривиально обрабатывать строки при сериализации, сохраняя в

потоке их содержимое.

Существует частный случай, когда сериализация выполняется тривиально. Для этого

граф должен иметь вид так называемого ориентированного дерева. В каждую вершину

дерева, отличную от корня, направлено единственное ребро, и существует

единственная вершина, в которую не ведет ни одного ребра, называемая корнем. В

таком случае сериализация может вестись от корня методом в глубину, а сериализация

полей-ссылок выполняется аналогично сериализации полей-значений – при

обнаружении ссылки на объект вместо нее в хранилище помещаются значения полей

ссылаемого объекта и некоторая дополнительная информация о типе объекта.

Например, если существуют ребра < A, B1>, ... < A, Bn >, функция сериализации S для

объекта A может быть определена как S(A) = < V(A), <S(B1), ... S(Bn)>>, где функция V –

значение полей-значений и строк указанного объекта.

Проблема сериализации графа объектов связана с тем, что ссылка на один и тот же

объект может быть значением поля у разных объектов (существуют ребра < A1, B> и

< A2, B >). Возможно также наличие циклов вида A → ... → A.

В этом случае в ходе сериализации объектам должны быть поставлены в соответствие

некоторые идентификаторы, и в хранилище отдельно сохраняется список объектов,

отмеченный идентификаторами, а при сериализации вместо ссылок записываются

идентификаторы ссылаемых объектов. Если A1, ... An – все вершины графа, то его

образом после сериализации будет множество {< idA1, S(A1) >, ... <idAn, S(An) >}, где

S(A) определена как S(A) = < V(A), <idB1, ... idBn> >, где B1, ... Bn – объекты, ссылки на

которые непосредственно содержатся в объекте A. В программе роль идентификатора

объекта выполняет его адрес, но вместо него обычно удобнее назначить некоторые

идентификаторы в процедуре сериализации для более легкого чтения человеком

полученного образа. В ходе сериализации нужно ввести список адресов уже

записанных объектов, как для ведения списка идентификаторов, так и для обнаружения

возможных циклов при обходе графа методом в глубину.

Page 59: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 4. Сериализация объектов. Способы сериализации в .NET Framework

54

Следует отметить, что результат сериализации дерева легко представим в виде XML,

когда содержимое каждого объекта представлено одним элементом с некоторым

набором атрибутов и вложенных элементов. Поэтому при рассмотрения проблемы

сериализации можно сформулировать рекомендацию, что классы, передаваемые между

удаленными компонентами, должны являться корнем дерева объектов. В частном

случае это дерево может быть вырожденным, то есть класс не содержит полей-ссылок

вообще.

4.2. Методы сериализации в .NET Framework

Microsoft .NET Framework 2.0 поддерживает четыре технологии удаленного

взаимодействия – Message Queuing, Enterprise Services, Remoting, Web services. Какая

бы технология не была выбрана, в любом случае передача данных между удаленными

компонентами является одним из основных моментов удаленного взаимодействия.

Данные могу передаваться как сообщение в очереди (MSMQ) или как параметры

удаленного вызова и результат вызываемой функции (остальные технологии). В любом

случае, для реализации распределенной системы необходимо определить процедуры

сериализации и десериализации для передаваемых классов.

Классы, производящие сериализацию и десериализацию в .NET Framework, называются

классами форматирования (formatters). Не считая служащего для обеспечения

совместимости ActiveXMessageFormatter, в .NET Framework выделяется три различных

независимых класса форматирования: XmlSerializer, SoapFormatter, BinaryFormatter.

Они используются как для записи и чтения объектов из потоков ввода-вывода, так и

для построения распределенных систем:

– технология веб-служб ASP.NET использует XmlSerializer;

– технология Remoting использует SoapFormatter, BinaryFormatter или созданный

пользователем класс;

– при работе с сообщениями MSMQ используется XmlSerializer (через

XMLMessageFormatter), или BinaryFormatter (через BinaryMessageFormatter), или

созданный пользователем класс;

– технология Enterprise Services основана на Remoting и использует

BinaryFormatter.

Page 60: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 4. Сериализация объектов. Способы сериализации в .NET Framework

55

Можно провести классификацию возможных методов сериализации по следующим

основным признакам.

1. Классификация по виду обрабатываемого графа:

– универсальные методы: граф произвольного вида с циклами;

– произвольный ациклический граф;

– дерево.

2. Классификация по формату хранения информации в хранилище:

– бинарные методы, использующие двоичный формат хранения данных, который

не пригоден для чтения человеком без использования специальных средств;

– текстовые методы, используют XML или иные текстовые форматы, пригодные

для чтения или редактирования человеком при использовании текстового

редактора.

3. Классификация по спецификации формата данных, полученного в результате

сериализации:

– закрытые методы: спецификация задается только при помощи интерфейсов всех

классов, объекты которых образуют граф; в этом случае обе удаленные

компоненты должны быть созданы на одной языковой платформе, причем обе

стороны должны иметь как минимум описание интерфейса сериализуемых

классов;

– открытые методы: спецификация может быть задана в виде общепринятого

формата, например схемы XSD.

По данной классификации XmlSerializer реализует открытый текстовый

неуниверсальный метод, BinaryFormatter – закрытый двоичный универсальный метод,

SoapFormatter – текстовый открытый метод. Дополнительной особенностью каждого из

классов, за исключением BinaryFormatter, является ограничения на классы, которые

подлежат сериализации. Данные ограничения будут рассмотрены далее.

Page 61: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 4. Сериализация объектов. Способы сериализации в .NET Framework

56

4.3. Класс сериализации XmlSerializer

Класс System.Xml.Serialization.XmlSerializer реализует открытый текстовый метод

сериализации, использующий XML в качестве базового формата хранения и схемы

XML для спецификации документа с результатом сериализации. Данный класс

используется, в частности, в веб-службах ASP.NET и при работе с очередями

сообщений MSMQ. Класс XmlSerializer порождает представление объекта на языке

XML, легко читаемое и модифицируемое человеком или программами. Класс

XmlSerializer позволяет управлять соответствием класса и схемы XML при помощи

специальных атрибутов полей классов. Благодаря открытой спецификации формата

хранения, при использовании XmlSerializer возможна десериализация в тип данных,

отличный от исходного, как и написание взаимодействующих компонент на основе

средств разработки.

Схема XML может быть получена из описания класса, если имеется информация о всех

типах объектов, которые могут содержаться во всех полях каждого класса. В частности,

это означает что для класса общего вида (generic) XML-схема в общем случае не может

быть получена. Однако и для конкретных классов самого описания типа недостаточно

для создания схемы. Например, в поле вида IVehicle vehicleValue может содержаться

объект любого класса, реализующий интерфейс IVehicle. Данная проблема решается в

CLI благодаря системе атрибутов. Например, используя атрибут

System.XML.Serialization.XmlAttrbute, можно перечислить все возможные типы

объектов данного поля. Хотя такой подход не идеален с точки зрения

объектно-ориентированного подхода, он дает возможность строго специфицировать

содержимое XML-файла. Ниже приведен пример использования атрибута

XmlAttrbuteAttrbute.

[System.XML.Serialization.XmlAttrbute(typeof(Car)), System.XML.Serialization.XmlAttrbute(typeof(Bike)), System.XML.Serialization.XmlAttrbute(typeof(LongTruck))] IVehicle vehicleValue;

Таким образом, при использовании некоторых дополнительных метаданных, для

классов CLI может быть построена соответствующая им схема XML, которая будет

корректной для организованного древовидно графа объектов.

Page 62: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 4. Сериализация объектов. Способы сериализации в .NET Framework

57

Используемый классом XmlSerializer метод сериализации имеет ряд недостатков.

Во-первых, он не является универсальным: сериализуемый им граф объектов не может

содержать циклы. Во-вторых, он полагает, что граф объектов является деревом и

записывает значение полей объекта на место их ссылки. В результате десериализации

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

ребер (рис 4.3). Поскольку на этапе построения XML-схемы нет никакой информации о

каких-либо объектах, а только описания полей и свойств классов, то предполагается,

что на каждый объект сериализуемого графа, отличный от его корня, существует

единственная ссылка в поле какого-либо другого объекта этого же графа.

Исходный граф

Сериализация Хранилище

root child 1

child 2

Исходный граф объектов Копия графа объектов

Результат десериализации

root child 1

child 2 копия child 2

Десериализация

Рисунок 4.3. Применение XmlSerializer к произвольному графу объектов

Наибольшей трудностью при использовании класса XmlSerializer являются

предъявляемые им требования к сериализуемым классам. В .NET Framework 2.0

XmlSerializer позволяет сериализовать публичные классы, имеющие конструктор без

параметров типа public и отвечающие одному из следующих требований.

1. Класс реализует интерфейс IXMLSerializable. В этом случае XMLSerializer просто

использует при сериализации методы класса GetSchema, ReadXml, WriteXml.

2. Класс реализует интерфейс System.Collections.IEnumerable, но не реализует

ICollection и содержит публичный метод Add c единственным параметром, имеющим

тип, совпадающий с типом результата свойства IEnumerator.Current метода

GetEnumerator сериализуемого объекта. Такой класс сериализуется через вызовы класса

Page 63: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 4. Сериализация объектов. Способы сериализации в .NET Framework

58

IEnumerator, возвращаемого методом GetEnumerator, а его публичные поля и свойства

не сериализуются.

3. Класс реализует интерфейс System.Collections.ICollection, но не реализует

IEnumerable. Для такого класса осуществляется сериализация только свойства Item и

публичных полей, реализующих интерфейс ICollection. Другие публичные поля и

свойства не сериализуются.

4. Класс реализует интерфейсы ICollection и IEnumerable, имеет публичное

индексированное свойство Item c целым индексом и публичное целое свойство Count.

Тип, принимаемый методом Add, должен быть типом свойства Item или одним из его

предков. Другие публичные поля и свойства не сериализуются.

5. Класс не реализует ни один из интерфейсов IXMLSerializer, IEnumerable, ICollection

и имеет атрибут System.SerializableAttribute. В этом случае будут сериализованы

публичные свойства и поля класса с учетом специальных атрибутов, управляющих

процессом сериализации.

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

методов, get и set. Кроме того, если класс не использует собственную процедуру

сериализации, то он не должен иметь свойств или полей типа интерфейс или

многомерных массивов, вместо них следует использовать вложенные массивы.

// недопустимо в сериализуемом автоматически классе public int[,] data = new int[2,2];

Page 64: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 4. Сериализация объектов. Способы сериализации в .NET Framework

59

К сожалению, классы FCL, реализующие интерфейс IDictionary, не удовлетворяют этим

требованием (их метод Add имеет два параметра). Поэтому главным практическим

недостатком XmlSerializer является неспособность самостоятельно обрабатывать типы,

реализующие интерфейс System.Collections.IDictionary, использующиеся для хранения

пар ключ–значение. В частности, к ним относится популярный класс

System.Collections.Hashtable. Однако можно создать собственный класс, включающий в

себя поле типа Hashtable и реализующий интерфейс IXmlSerializable. XMLSerializer

может обрабатывать следующие важные на практике классы из FCL:

– System.XML.XmlNode и его наследники System.XML.XmlDocumеnt и

System.XML.XmlElement, причем сериализация XML-документа является этим

же XML-документом (с точностью до форматирования, не влияющего на

содержимое документа);

– класс System.Data.Dataset;

– классы общего вида (generic classes) из System.Collections.Generic, такие как List

или Queue, кроме Dictionary и SortedDictionary.

Как было указанно ранее, для большинства классов можно использовать описанные в

System.Xml.Serialization атрибуты сериализации, которые позволяют установить связь

между полями класса и атрибутами или элементами XML, отвечающими ему. Также

можно использовать атрибут NonSerializedAttribute для публичных полей или свойств,

не подлежащих сериализации.

В качестве примера рассмотрим пример общего класса, реализующий интерфейс

IXMLSerializable для сериализации коллекции пар из ключа и значения какого-либо

фиксированного типа. Класс содержит два объекта сериализации – один для обработки

строк, второй для типа значений.

Page 65: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 4. Сериализация объектов. Способы сериализации в .NET Framework

60

using System.Xml; using System.Collections; using System.Xml.Serialization; public class DictionaryCollection<TValue> : DictionaryBase, IXmlSerializable { private XmlSerializer valueSerializer; private XmlSerializer keySerializer; public DictionaryCollection() { keySerializer = new XmlSerializer(typeof(String)); valueSerializer = new XmlSerializer(typeof(TValue)); }

Класс имеет индексируемое свойство, отвечающее за получение значения по его

ключу, и метод добавления ключа и значения в коллекцию.

public TValue this[string key] { get { return (TValue)this.Dictionary[key]; } set { this.Dictionary[key] = value; } } public void Add(String key, TValue value) { this.Dictionary.Add(key, value); }

Для сериализации классом XMLSerializer класс имеет два метода – WriteXml и

ReadXML.

public void WriteXml(System.Xml.XmlWriter writer) { foreach (String key in this.Dictionary.Keys) { keySerializer.Serialize(writer, key); valueSerializer.Serialize(writer, this[key]); } } public void ReadXml(System.Xml.XmlReader reader) { reader.Read(); while (reader.NodeType != XmlNodeType.EndElement) { String key = (String) keySerializer.Deserialize(reader); TValue value = (TValue) valueSerializer.Deserialize(reader); reader.MoveToContent(); Add(key, value); } }

Page 66: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 4. Сериализация объектов. Способы сериализации в .NET Framework

61

Метод GetSchema необходим для генерации XSD-файла для данного класса. Поскольку

создание такой схемы для класса общего вида невозможно, метод не реализован.

public System.Xml.Schema.XmlSchema GetSchema() { return null; } } // DictionaryCollection<TValue>

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

строка-число в файл.

DictionaryCollection<Int32> dictionary = new DictionaryCollection<Int32>(); XmlSerializer serializer = new XmlSerializer(typeof(DictionaryCollection<Int32>)); dictionary.Add("key1", 10); dictionary.Add("key2", 20); using (StreamWriter writer = new StreamWriter("sample.xml")) { XmlTextWriter xmlWriter = new XmlTextWriter(writer); xmlWriter.Formatting = Formatting.Indented; serializer.Serialize(xmlWriter, dictionary); }

Для большинства применений нет нужды реализовывать интерфейс IXmlSerializable, и

достаточно использовать многочисленные атрибуты из пространства

System.Xml.Serialization, управляющие XML-сериализацией. К ним, в частности,

относятся XmlAttributeAttribute, XmlElementAttribute, XmlArrayAttribute,

XmlArrayItemAttribute, XmlIgnoreAttribute. Предназначение последнего атрибута

наиболее простое, остальные же нуждаются в пояснении.

Атрибуты XmlAttributeAttribute и XmlElementAttribute обычно применяют к скалярным

полям класса. Два их опционных параметра – название атрибута или элемента XML с

результатом сериализации, а также тип объекта. XmlElementAttribute может быть

применен только к примитивным типам и строкам. Если тип объекта, хранящимся в

поле, совпадает с указанным в типе или в атрибуте поля, то в XML-файле не будет

использоваться атрибут типа xsi:type для указания типа объекта.

Атрибуты XmlArrayAttribute, XmlArrayItemAttribute используются для контейнерных

классов. К ним относятся массивы, коллекции (например, ArrayList) и коллекции

общего вида (например, List<T>). В этом случае атрибут XmlArray аналогичен

Page 67: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 4. Сериализация объектов. Способы сериализации в .NET Framework

62

атрибуту XmlAttribute для скалярных классов, а XmlArrayItem указывает все

возможные типы элементов массива или списка и соответствующие им имена

элементов XML. Для корректной обработки контейнера в атрибутах XmlArrayItem

должны быть указаны все типы объектов, которые могут храниться в контейнере. Если

в контейнере хранится только тип, указанный явным образом при его объявлении, то

данный атрибут не обязателен. Таким образом, если в списке List<Person> persons

хранятся только объекты типа Person, то атрибут XmlArrayItem не обязателен.

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

сериализации.

// Файл figures.cs using System; using System.IO; using System.Xml.Serialization; using System.Collections.Generic; public abstract class GeomFigure { }

Абстрактный класс фигуры имеет двух наследников, представляющих точку и прямую

на плоскости. Конструктор по умолчанию необходим для десериализации объектов.

Page 68: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 4. Сериализация объектов. Способы сериализации в .NET Framework

63

public class GeomPoint: GeomFigure { private double xField, yField; [XmlAttribute("X")] public double X { get {return xField;} set {xField = value;} } [XmlAttribute("Y")] public double Y { get {return yField;} set {yField = value;} } public GeomPoint() { } public GeomPoint(double x, double y) { this.X = x; this.Y = y; } public override string ToString() { return String.Format("({0}, {1})", X, Y); } } public class GeomLine: GeomFigure { private GeomPoint aField, bField; public GeomPoint A { get {return aField;} set {aField = value;} } public GeomPoint B { get {return bField;} set {bField = value;} } public GeomLine() { } public GeomLine(GeomPoint a, GeomPoint b) { this.A = a; this.B = b; }

Page 69: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 4. Сериализация объектов. Способы сериализации в .NET Framework

64

public override string ToString() { return String.Format("{0} {1}", A, B); } }

В списке фигур используются атрибуты XmlArrayItem для описания всех возможных

типов фигур.

public class GeomFigures { private List<GeomFigure> figuresField; [XmlArrayItem("Point", typeof(GeomPoint)), XmlArrayItem("Line", typeof(GeomLine))] public List<GeomFigure> Figures { get { return figuresField;} } public GeomFigures() { figuresField = new List<GeomFigure>(); } } public class App { public static void Main() { GeomFigures figures = new GeomFigures(); figures.Figures.Add(new GeomPoint(2, -1)); figures.Figures.Add(new GeomLine(new GeomPoint(-1, -1), new GeomPoint(2, 2))); XmlSerializer serializer = new XmlSerializer(typeof(GeomFigures)); using (StreamWriter writer = new StreamWriter("figures.xml")) { serializer.Serialize(writer, figures); }; } }

В результате выполнения этого примера будет создан следующий XML-файл.

<?xml version="1.0" encoding="utf-8"?> <GeomFigures xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Figures> <Point X="2" Y="-1" /> <Line> <A X="-1" Y="-1" /> <B X="2" Y="2" /> </Line> </Figures> </GeomFigures>

Page 70: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 4. Сериализация объектов. Способы сериализации в .NET Framework

65

Важное применение атрибутов заключается в том, что они позволяют описать

соответствие класса XSD-схеме получаемого в ходе сериализации документа. В состав

.NET Framework входит утилита xsd.exe, позволяющая выполнять три основные задачи:

– создание частичного (partial) описания класса на С# по схеме XSD;

– создание схемы XSD по классу С#;

– создание схемы XSD по образцу XML-файла (правильность зависит от полноты

образца).

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

файл schema0.xsd со схемой XML.

xsd.exe figures.exe /type:GeomFigures

Сама схема будет иметь следующий вид.

<?xml version="1.0" encoding="utf-8"?> <xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="GeomFigures" nillable="true" type="GeomFigures" /> <xs:complexType name="GeomFigures"> <xs:sequence> <xs:element minOccurs="0" maxOccurs="1" name="Figures" type="ArrayOfChoice1" /> </xs:sequence> </xs:complexType> <xs:complexType name="ArrayOfChoice1"> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element minOccurs="1" maxOccurs="1" name="Point" nillable="true" type="GeomPoint" /> <xs:element minOccurs="1" maxOccurs="1" name="Line" nillable="true" type="GeomLine" /> </xs:choice> </xs:complexType> <xs:complexType name="GeomPoint"> <xs:complexContent mixed="false"> <xs:extension base="GeomFigure"> <xs:attribute name="X" type="xs:double" use="required" /> <xs:attribute name="Y" type="xs:double" use="required" /> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="GeomFigure" abstract="true" /> <xs:complexType name="GeomLine"> <xs:complexContent mixed="false"> <xs:extension base="GeomFigure"> <xs:sequence> <xs:element minOccurs="0" maxOccurs="1" name="A" type="GeomPoint" /> <xs:element minOccurs="0" maxOccurs="1" name="B" type="GeomPoint" /> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> </xs:schema>

Page 71: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 4. Сериализация объектов. Способы сериализации в .NET Framework

66

Следующая команда создаст по XML-схеме файл на языке C#, который при

необходимости можно использовать для десериализации созданного в примере

XML-файла вместо оригинального файла примера.

xsd.exe schema0.xsd /classes

Таким образом, при использовании для обмена данными между компонентами класса

XmlSerializer можно как создать схему по сериализуемому классу и представить ее в

качестве спецификации передаваемых данных, так и сгенерировать на языке C# код

описания публичных свойств класса из XSD-схемы. На рисунке 4.4 показана схема

применения XML-сериализатора в распределенных системах. В зависимости от

применения класса XMLSerializer схема XML может передаваться как отдельный

документ, специфицирующий формат обмена сообщениями между компонентами, или

входить в описание интерфейса программной компоненты на языке WSDL.

Компонента 2Компонента 1

СхемаXML

Сериализуемыйкласс

Объект

Класс длядесериализации

Объект

Каналпередачиданных

ДесериализацияСериализация

Рисунок 4.4. Использование схем XML для сериализации объектов

Особенностью класса XMLSerializer является то, что в ходе работы он создает сборки с

кодом сериализации для каждого обрабатываемого класса при вызове его

конструктора. Например, в описанном ранее примере в памяти будет создана сборка с

именем GeomFigures.XmlSerializers, что приводит к определенной однократной

задержке.

Если такая задержка нежелательна (например, программа не совершает повторяющейся

сериализации одного и того же класса, но желает при этом осуществлять операции

Page 72: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 4. Сериализация объектов. Способы сериализации в .NET Framework

67

максимально быстро), то при помощи утилиты sgen.exe можно заранее создать такие

сборки и затем подключить их к проекту.

Резюмируя краткое описание XMLSerializer, следует отметить, что несмотря на

отдельные сложности его применения к некоторым классам, его использование

позволяет создать открытое взаимодействия удаленных компонент со спецификацией

сериализуемых классов в виде схемы XSD. При использовании XMLSerializer можно

также рекомендовать вместо сложных структур данных использовать классы

XmlDocument или Dataset, особенно если такие структуры включают

неподдерживаемые XML-сериализацией классы. Сериализуемый тип может быть

классом общего вида, но невозможность создания для таких классов схемы

XML-документа ограничивают их применение. Особенностью класса XmlSerializer

является жесткая привязка к десериализуемому типу данных, обычно передаваемому

ему в качестве аргумента конструктора.

4.4. Классы сериализации SoapFormatter и BinaryFormatter

Класс сериализации System.Runtime.Serialization.Formatters.Soap.SoapFormatter

используется исключительно в среде .NET Remoting, а класс

System.Runtime.Serialization.Formatters.Binary.BinaryFormatter может также

использоваться в среде MSMQ вместо XMLSerializer. Оба класса форматирования по

приведенной классификации являются универсальными. Класс форматирования

BinaryFormatter реализует двоичный закрытый метод сериализации, класс

SoapFormatter – текстовый и открытый, основанный на спецификации кодирования

SOAP-RPC (пространство имен http://schemas.xmlsoap.org/soap/encoding/).

При разработке .NET Framework 2.0 разработчики по некоторым данным собирались

придать классу SoapFormatter статус устаревшего. Класс SoapFormatter не

поддерживает одно из важных нововведений – параметризированные типы данных

(generic types).

Оба указанных класса в простейшем случае при сериализации сохраняют все поля

класса (но не его свойства), вне зависимости от их видимости. Поля, имеющие атрибут

System.NonSerializeAttribute, игнорируются. Класс должен иметь атрибут

Page 73: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 4. Сериализация объектов. Способы сериализации в .NET Framework

68

System.SerializableAttribute. В ходе сериализации класса форматирования используют

методы класса System.Runtime.Serialization.FormatterServices. Сериализуемый класс

должен содержать конструктор без параметров, который вызывается при создании

нового объекта в ходе десериализации.

Если же обрабатываемый класс реализует интерфейс ISerializable, то он сериализуется

вызовом метода GetObjectData(SerializationInfo info, StreamingContext context) этого

интерфейса, внутри которого обычно так же вызываются методы FormatterServices.

Десериализация таких классов осуществляется вызовом конструктора

ISerializable(SerializationInfo info, StreamingContext context), заполняющего поля

объекта значениями из info.

О завершении своей десериализации объект может получить уведомление, реализовав

интерфейс System.Runtime.Serialization.IDeserializationCallback с единственным

методом OnDeserialization.

Полученный таким образом на первом шаге сериализации объект класса

SerializationInfо содержит имена и значения сериализуемых полей. Рассматриваемые

классы форматирования, реализующие интерфейс IFormatter, преобразуют эти имена в

некоторый вид, передаваемый между доменами приложения через потоки ввода-

вывода.

Рассмотрим пример создания класса с интерфейсом ISerializable и собственным

механизмом сериализации.

using System; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters; using System.Runtime.Serialization.Formatters.Binary; using System.Reflection; [Serializable] public class Person : ISerializable { public String name; public Person() { }

Page 74: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 4. Сериализация объектов. Способы сериализации в .NET Framework

69

Метод GetObjectData используется на первом шаге сериализации класса. В ходе его

работы в объект класса SerializationInfo добавляется информация о полях класса,

подлежащих сериализации. Для получения метаданных о полях класса используется

статический метод GetSerializableMembers класса FormatterServices.

public void GetObjectData(SerializationInfo info, StreamingContext context) { Type thisType = this.GetType(); MemberInfo[] serializableMembers = FormatterServices.GetSerializableMembers(thisType, context); foreach (MemberInfo serializableMember in serializableMembers) { // Не обрабатывать поля с аттрибутом NonSerializedAttribute if (!(Attribute.IsDefined(serializableMember, typeof(NonSerializedAttribute)))) { info.AddValue(serializableMember.Name, ((FieldInfo)serializableMember).GetValue(this)); } } }

Для проведения десериализации класс содержит конструктор специального вида,

заполняющий поля класса значениями из объекта класса SerializationInfo.

protected Person(SerializationInfo info, StreamingContext context) { Type thisType = this.GetType(); MemberInfo[] serializableMembers = FormatterServices.GetSerializableMembers(thisType, context); foreach (MemberInfo serializableMember in serializableMembers) { FieldInfo fieldInformation = (FieldInfo)serializableMember; if (!(Attribute.IsDefined(serializableMember, typeof(NonSerializedAttribute)))) { fieldInformation.SetValue(this, info.GetValue(fieldInformation.Name, fieldInformation.FieldType)); } } } } // Person

Ниже приведен пример использования созданного класса Person.

public class SampleApp { public static void Main() { using (Stream stream = new MemoryStream()) { IFormatter formatter = new BinaryFormatter();

Page 75: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 4. Сериализация объектов. Способы сериализации в .NET Framework

70

Person person = new Person(); person.name = "Иван"; Console.WriteLine("Сохранено: {0}", person.name); formatter.Serialize(stream, person); stream.Position = 0; Person personRestored = (Person) formatter.Deserialize(stream); Console.WriteLine("Восстановлено: {0}", personRestored.name); } } }

Классы форматирования имеют механизм, позволяющий изменить процедуры

сериализации и десериализации для объектов некоторого класса и его потомков. Это

необходимо, в частности, при использовании удаленных объектов, которые

маршализируются по ссылке и не пересекают границы домена приложения. Такие

объекты находятся на сервере, а на стороне клиента для их использования должен быть

создан некоторый посредник, реализующий весь интерфейс удаленного объекта,

включая доступ к его полям и свойствам. Для реализации маршализации по ссылке к

объекту форматирования через поле SurrogateSelector можно присоединить класс,

реализующий интерфейс System.Runtime.Serialization.ISurrogateSelector. Он должен

связывать тип удаленного объекта со специальной процедурой его сериализации и

десериализации. Использование этого механизма в .NET Remoting приводит к тому, что

наследники класса MarshalByRefObject не покидают своего домена приложения. При

использовании же BinaryFormatter в среде MSMQ наследники MarshalByRefObject

сериализуется обычным образом, поскольку использующий объекты форматирования

класс BinarryMessageFormatter не создает связанный с типом MarshalByRefObject

объект класса SurrogateSelector.

Использование класса BinaryFormatter является наиболее эффективным и

универсальным, но и самым закрытым способом сериализации. Этот класс позволяет

передавать между доменами приложения произвольный граф объектов, но при его

использовании распределенная система теряет свойство открытости. В случае

применения этого класса взаимодействующие компоненты могут быть созданы только

на платформе CLI, причем обоим сторонам необходимо иметь сборку с сериализуемым

типом. При использовании в качестве параметров типов из стандартной библиотеки

или использующих их классов желательно, чтобы обе стороны были реализованы на

одной версии CLI. Поэтому для передачи сложных типов лучше всего использовать

Page 76: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 4. Сериализация объектов. Способы сериализации в .NET Framework

71

XML. Однако, стандартный класс System.Xml.XmlDocument не может быть

сериализован классами BinaryFormatter и SoapFormatter, поскольку данный класс по

неясным причинам не имеет атрибута Serializable. Для сериализации объектов класса

XmlDocument проще всего преобразовать его в строку, и затем сериализовать ее.

Можно так же создать наследника XmlDocument, который будет реализовывать

интерфейс ISerializable.

Ниже приводится пример вспомогательного класса с двумя статическими методами,

преобразующими объект класса XmlDocument в строку и наоборот. Поскольку метод

XmlDocument.ToString() против ожиданий не возвращает текст XML-документа и у

него нет метода, обратного LoadXml, то следует использовать класс StringWriter.

// SevaXmlUtils.cs using System; using System.IO; using System.Xml; namespace Seva.Xml { public static class XmlUtils { public static String XmlToString(XmlDocument xml) { StringWriter xmlLine = new StringWriter(); xml.Save(xmlLine); return xmlLine.ToString(); } public static XmlDocument XmlFromString(String xmlLine) { XmlDocument xml = new XmlDocument(); xml.LoadXml(xmlLine); return xml; } } }

4.5. Выводы по использованию классов сериализации

Все классы сериализации библиотеки .NET Framework имеют свои особенности и

ограничения, что может вызвать значительные изменения в программном коде при

переходе с одной промежуточной среды на другую. Один из способов борьбы с этой

проблемой состоит в отказе от сериализации нетривиальных классов (содержащих

что-либо, кроме примитивных типов-значений и строк), и особенно сложных

списочных структур. Вместо них, вероятно, следует использовать наборы данных

Page 77: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 4. Сериализация объектов. Способы сериализации в .NET Framework

72

(класс System.Data.Dataset) или документы XML (класс System.Xml.XMLDocument).

Хотя такой способ может являться не совсем удобным для разработчика, он дает залог

создания независимого от класса форматирования программного кода.

Page 78: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 5. MSMQ – промежуточная среда обмена сообщениями

73

Тема 5. Microsoft Message Queuing (MSMQ) – промежуточная среда обмена сообщениями

5.1. Служба обмена сообщениями MSMQ

В настоящий момент существует несколько основных разработок в области

промежуточного программного обеспечения для работы с очередями сообщений.

Наиболее известными разработками являются такие системы очередей сообщений, как

MSMQ, Sun Java System Message Queue, IBM MQSeries, Oracle Advanced Queing.

Промежуточная среда MSMQ – разработка Microsoft для асинхронной передачи

сообщений внутри локальной сети, впервые появившаяся в составе операционной

системы Windows NT. В настоящее время последней является версия MSMQ 3.0,

включенная в Windows XP PE и 2003 Server, достаточно актуальна так же версия 2.0,

включенная в состав операционной системы Windows 2000.

Служба MSMQ позволяет произвольному набору приложений добавлять сообщения в

некоторую очередь, и произвольному же набору – читать сообщения из очереди.

Приложению, использующему MSMQ, доступны следующие основные операции:

– добавить сообщение в очередь;

– извлечь первое сообщение из очереди;

– установить обработчик, вызываемый при появлении сообщения.

Структура сообщения определяется приложением, и может быть произвольной, с

ограничением на размер одного сообщения (2Мб для MSMQ 2.0). Далее будет

рассмотрено использование MSMQ 2.0/3.0 при разработке приложений с

использованием .NET Framework.

Page 79: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 5. MSMQ – промежуточная среда обмена сообщениями

74

При отправке сообщения с использованием MSMQ посылающему приложению

необходимо указать имя компьютера и очереди, в которую его необходимо доставить.

После вызова приложением функции отправки сообщение сохраняется в локальной

исходящей очереди. Затем MSMQ определяет имя компьютера и очереди, куда

необходимо передать сообщение. Возможны следующие случаи:

– сообщение доставляется сразу в указанную отправителем очередь (прямая

доставка);

– сообщение посылается в некоторую промежуточную очередь, определяемую

маршрутизатором службы сообщений;

– MSMQ определяет, что сообщение требуется разослать в несколько очередей

(возможность поддерживается начиная с MSMQ 3.0).

Компьютер с рассылающимсообщения приложением

Приложение

MSMQИсходящая очередь

Компьютерс промежуточнойочередью сообщений

MSMQ

Входящаяочередь

Исходящаяочередь

Компьютер с очередью – пунктомназначения сообщений

MSMQ

Исходящаяочередь

Входящаяочередь

Компьютер с читающимсообщения приложением

MSMQ

Приложение

Рисунок 5.1. Пересылка сообщения в промежуточной среде MSMQ

После определения имени компьютера с очередью назначения, MSMQ проверяет

доступность компьютера (пакетом UDP) и в случае ответа сразу пытается отправить

ему сообщение, повторяя попытки с интервалом по умолчанию 5 секунд. Если

сообщение не удается отправить, то обычно каждые 5 минут служба сообщений

Page 80: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 5. MSMQ – промежуточная среда обмена сообщениями

75

пытается найти новый пункт назначения сообщения, используя маршрутизацию

MSMQ. Процесс пересылки сообщения между компьютерами повторяется, пока оно не

достигнет очереди назначения. С момента поступления сообщения в указанную при

отправке очередь любое использующее MSMQ приложение с необходимыми правами

доступа может прочитать это сообщение (рис 5.1).

Таким образом, MSMQ поддерживает асинхронную передачу сообщений, при которой

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

функционировать в один и тот же момент времени.

Благодаря службе маршрутизации сообщений возможно создание моста между MSMQ

и аналогичной технологией IBM – IBM Websphere MQ (ранее MQSeries).

Websphere MQ может использоваться и напрямую программами .NET Framework,

однако обычно это менее удобно, чем использование MSMQ, и может быть связано с

дополнительными затратами – служба MSMQ уже входит в большинство систем

семейства Windows. Использование моста MSMQ-MQSeries позволяет создавать

гетерогенные распределенные системы на основе обмена сообщениями, поскольку

технология IBM MQ является изначально межплатформенной.

Приложение может вести поиск нужной ему очереди по ряду критериев. Это возможно

при использовании механизма общих очередей в Microsoft Message Queuing, что

требует развертывания Microsoft Active Directory.

5.2. Инфраструктура, необходимая для использования MSMQ

Служба MSMQ может работать как в составе домена Active Directory, так и при

отсутствии такого домена, но во втором случае невозможно использовать ряд

возможностей MSMQ, а именно:

– не поддерживается шифрование передаваемых сообщений;

– не поддерживаются общие очереди и механизмы их обнаружения;

– не поддерживается маршрутизация сообщений и групповая рассылка

сообщений.

Page 81: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 5. MSMQ – промежуточная среда обмена сообщениями

76

В MSMQ существуют два вида очередей – общие (public) и частные (private). Как

частные, так и общие очереди могут либо использовать, либо не использовать

транзакции, что задается при создании очереди и не может быть изменено в

дальнейшем.

Информация об общих очередях публикуется в службе каталогов Active Directory. Путь

к общей очереди имеет вид ComputerName\QueueName, возможна также запись пути в

виде .\QueueName для очереди на локальном компьютере. Посылающее сообщение

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

Возможна также проверка приложением наличия общей очереди на удаленном

компьютере и ее создание. Для использования общих очередей требуется

развертывание MSMQ в рамках домена Active Directory.

Частные очереди доступны как при наличии домена, так и при его отсутствии. Путь к

такой очереди имеет вид ComputerName\Private$\QueueName или .\Private$\QueueName

для локального компьютера. Приложение, не имеет возможности ни создать, ни

проверить наличие частной очереди на удаленном компьютере при использовании

средств библиотеки FCL.

В MSMQ 3.0 появилась возможность посылать сообщения в общую или частную

очередь по протоколу HTTP при наличии на удаленном компьютере службы Internet

Information Services (IIS). В этом случае используется путь к очереди вида

http://domain.name/msmq/private$/queuename. Посылающий сообщения компьютер так

же должен поддерживать HTTP для MSMQ, иначе он не сможет получать какие-либо

ответы. При чтении сообщений использовать HTTP-формат имени очереди

невозможно.

MSMQ поддерживает два вида имени очереди – прямой вид (direct name) и путь к

очереди (path name). При использовании частных очередей на удаленном компьютере

обычно необходимо использовать прямой путь к очереди. При использовании MSMQ

программами на .NET Framework прямой путь записывается следующим образом:

– Formatname:DIRECT=OS:server01\QueueName – прямое имя общей очереди на

компьютере server01;

Page 82: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 5. MSMQ – промежуточная среда обмена сообщениями

77

– Formatname:DIRECT=OS:ws02\Private$\QueueName – прямое имя частной

очереди на компьютере ws01;

– Formatname:DIRECT=TCP:192.168.30.1\private$\QueueName – прямое имя

частной очереди на компьютере с адресом 192.168.30.1.

Полноценная работа с очередями возможна только в пределах локальной или

виртуально частной сети, поскольку при чтении сообщений используется Windows RPC

(в частности, обращения на 135-й порт). Кроме этого, среда MSMQ не способна

функционировать при использовании NAT, за исключением случая HTTP-имени

очереди.

5.3. Применение службы сообщений MSMQ в распределенных системах

Рассмотрим возможности, предоставляемые службой MSMQ как промежуточным

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

распределенной системе.

1. Открытость. Требование выполняется частично: используя в качестве сообщения

XML-документ с заданной XSD-схемой, можно добиться открытости передаваемых

данных. Однако сама технология MSMQ является закрытой и одноплатформенной.

2. Безопасность. MSMQ поддерживает обычные для Windows NT списки управлением

доступа (ACL) для каждой очереди, а при наличии домена AD – прозрачное

шифрование сообщений. Таким образом, система, использующая MSMQ, может

являться безопасной при развертывании в пределах домена Active Directory.

3. Расширяемость. MSMQ позволяет создавать масштабирумые системы, поскольку не

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

Если время обработки сообщения удаленным приложением несравнимо больше

времени на его пересылку между очередями, то возможен линейный рост

производительности при увеличении числа компьютеров, считывающих и

обрабатывающих сообщения из очереди.

Page 83: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 5. MSMQ – промежуточная среда обмена сообщениями

78

4. Обеспечение целостности данных. MSMQ поддерживает внутренние транзакции,

распределенные транзакции среды COM+ и транзакции из System.Transactions.

Внутренние транзакции гарантируют, что некоторая последовательность операций

компоненты с очередями (например, получение сообщение и отправка ответа на него)

будет либо выполнена полностью, либо не выполнена вообще. Транзакции среды

COM+ и пространства имен System.Transactions используют координатор

распределенных транзакций (MS DTC). При их использовании в последовательность

операций, образующих транзакцию, кроме действий с очередями сообщений могут

входить операции с любыми службами, поддерживающие распределенные транзакции.

Кроме использования транзакций, для повышения надежности в MSMQ следует

использовать механизм восстанавливаемых сообщений, который повышает вероятность

того, что после принятия службой MSMQ сообщение не будет потеряно при аварийном

отключении питания. Распределенные транзакции COM+ будут рассмотрены в

следующем разделе.

5. Устойчивость. Основной особенностью MSMQ является возможность

использования нескольких читающих из очереди серверов. Кроме того, при

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

требуемой очередью. В силу этого при использовании очередей сообщений можно

избежать возникновения уникальной точки уязвимости.

Таким образом, с точки зрения требований к распределенной системе технология

MSMQ обладает многочисленными достоинствами. Однако данная технология

промежуточного ПО также имеет ряд важных недостатков.

1. Использование сообщений происходит непрозрачно для приложения, в отличие от

использования механизмов удаленного вызова.

2. MSMQ неудобно использовать вне LAN/VPN. Если какой-либо компоненте нужно

посылать сообщения по протоколу HTTP и при этом получать ответы с результатом

обработки своих сообщений, то вероятно предпочтительнее либо использовать VPN,

либо рассмотреть вариант использования синхронных удаленных вызовов.

Page 84: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 5. MSMQ – промежуточная среда обмена сообщениями

79

3. Для реализации модели «запрос-ответ» при использовании MSMQ предпочтительно

применять отдельную очередь ответов для каждой посылающей запросы компоненты.

Использование очередей, поддерживающих транзакции, отличается от использования

очередей без поддержки транзакций, причем использование внутренних и внешних

транзакций заметно отличается. Работа с частными очередями может отличаться от

работы с общими очередями. В свою очередь набор возможных операций с частными

локальных очередями (.\Private$\LocalQueueName) отличается от такового для

удаленных частных очередей (SomeComputer\Private$\RemoteQueueName). Поэтому при

использовании MSMQ, как и других промежуточных сред, рекомендуется создать

промежуточный слой взаимодействия с ним. Такой слой скрывал бы использование

очередей от вышестоящих компонент и позволял безболезненно перейти от частных к

общим очередям или наоборот, от внутренних транзакций ко внешним и упростил бы

переход на другое промежуточное программное обеспечение.

Службы очередей сообщений находят наилучшее применение в случаях, когда

какая-либо компонента распределенной системы посылает заявки, не требующие

ответа (модель «запрос»). Если же компонента получает некоторые ответы в

асинхронном режиме (модель «запрос-ответ»), то она должна, вероятно, поддерживать

собственный список ожидающих ответов заявок. Это означает наличие у компоненты

некоторого внутреннего состояния, которое должно сохраняться в течение длительного

времени. Наличие такого состояния является недостатком с точки зрения концепции

распределенной системы как набора сервисов, не сохраняющих историю своих

вызовов. С точки зрения этой концепции использующая MSMQ компонента либо

дожидается ответа сервера в течение одного своего вызова другими компонентами,

либо не нуждается в ответе вообще. В первом случае MSMQ предпочтительнее

технологий удаленного вызова в одном из в следующих случаев:

– обеим взаимодействующим компонентам известен только посредник

(компьютер с используемой очередью сообщений);

– заявки обрабатываются несколькими компьютерами параллельно;

– вызывающая компонента не знает внешний интерфейс удаленного вызова

вызываемой компоненты.

Page 85: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 5. MSMQ – промежуточная среда обмена сообщениями

80

Наличие хороших возможностей масштабирования у систем, использующих очереди

сообщений, может быть одним из доводов в пользу выбора такой технологии в

качестве промежуточной среды. В частности, одним из возможных приложений MSMQ

является создание вычислительных распределенных приложений, реализующих

какие-либо численные методы, поскольку в настоящий момент MSMQ является

практически единственным доступным при использовании .NET Framework средством

обмена сообщениями, которое может быть использовано для разработки параллельных

вычислительных приложений. Поскольку накладные расходы на использование

очередей сообщений достаточно существенны, то использование очередей сообщений

оправдано в тех случаях, когда выполняемая на удаленном компьютере задача

выполняется значительное время (порядка десятых долей секунды и более).

Отдельным вопрос при использовании службы MSMQ является решение такой

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

каком-либо заданном компьютере, она является узким местом с точки зрения

устойчивости и расширяемости системы. К сожалению, при использовании MSMQ из

.NET Framework нет возможности узнать, например, среднюю длину или время

нахождения заявок в очереди для выбора приложением одной из нескольких очередей.

Для увеличения устойчивости системы можно было бы выбирать работающую очередь

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

очередью видимо скорее имеет смысл реализовать посылку тестового пакета ICMP, чем

дожидаться от MSMQ известия о невозможности послать сообщение в очередь. Таким

образом, решение вопроса балансировки нагрузки на менеджеры очередей и

дублирование их функций в настоящий момент остается за разработчиком

распределенной системы.

5.4. Использование очередей сообщений MSMQ в .NET Framework

Для работы с очередями сообщений используются классы из пространства имен

System.Messaging. Класс System.Messaging.MessageQueue содержит три группы

методов.

1. Статические методы для администрирования очередей: Create, Delete, Exists, Purge.

Page 86: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 5. MSMQ – промежуточная среда обмена сообщениями

81

2. Методы поиска общих очередей: GetPublicQueues, GetPublicQueuesByLabel и

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

между несколькими менеджерами очередей в пределах Active Directory, если один из

них выходит из строя.

3. Методы для работы с сообщениями (Send, Receive, Peek и другие), в том числе

позволяющие использовать обработчик на завершение операции (BeginPeek,

BeginReceive).

При применении классов из System.Messaging возможно три варианта работы с

очередями сообщений:

– работа с очередями, не использующими транзакции;

– работа с очередями, поддерживающими транзакции, при использовании

внутренних транзакций MSMQ;

– работа с очередями, поддерживающими транзакции, при использовании

распределенных транзакций COM+.

Определенной трудностью при использовании MSMQ в .NET Framework являются

различия по использованию разных видов очередей. В частности, для очередей без

транзакций можно установить обработчик на завершение приема сообщения, а для

очередей с транзакциями этот способ неприемлем, поскольку само чтение сообщения

должно быть оформлено как часть транзакции. Для обоих видов очередей можно

поставить обработчик на появление сообщения в очереди, что и является

рекомендованным способом.

Для сериализации и десериализации сообщений MSMQ могут использоваться классы

XMLMessageFormatter или BinaryMessageFormatter из пространства имен

System.Messaging. Класс XMLMessageFormatter использует класс

System.Xml.Serialization.XmlSerializer, описанный ранее в теме о сериализации,

поэтому при использовании XMLMessageFormatter должны учитываться все

особенности использования класса XmlSerializer. Класс BinaryMessageFormatter

аналогичным способом использует для сериализации класс BinaryFormatter.

Page 87: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 5. MSMQ – промежуточная среда обмена сообщениями

82

Поскольку классы BinaryFormatter и XmlSerializer имеют различные ограничения на

сериализуемые классы и используют совершенно различные процедуры сериализации,

в нетривиальном случае переход BinaryMessageFormatter на XMLMessageFormatter или

наоборот может привести к определенным изменениям в исходном коде программных

компонент. Рекомендованным для использования с MSMQ следует считать

XMLMessageFormatter. Его применение позволяет создать XSD-схему для

передаваемого сообщения. При использовании MSMQ ни значительно меньший объем

сообщения, создаваемого классом BinaryMessageFormatter, ни его меньшее время

работы не является принципиальными факторами.

Ниже рассмотрено вспомогательное пространство имен с классами общего вида,

реализующими модель «запрос-ответ» при использовании внутренних транзакций

MSMQ (рис 5.2).

Клиенты MSMQЗапросы

ОтветыСерверы

Рисунок 5.2. Обслуживание запросов клиентов при использовании MSMQ

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

System.Messaging и пространство имен с коллекциями общего вида.

using System; using System.Messaging; using System.Collections.Generic;

Классы используют два делегата общего вида, которые будут связаны с событиям

обработки сообщения сервером и получения ответа клиентом.

namespace Seva.Msmq { // типы очередей enum QueueType {NonTransactional, Transactional}; // типы классов форматирования enum QueueFormatter {Binary, Xml};

Page 88: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 5. MSMQ – промежуточная среда обмена сообщениями

83

// делегат общего вида для обработки сервером сообщений клиента delegate AnswerType ProcessRequestEventHandler <RequestType, AnswerType>(Object sender, RequestType request, MessageQueue queueResponse); // делегат общего вида для обработки ответов сервера клиентом delegate void ProcessAnswerEventHandler<RequestType, AnswerType> (Object sender, RequestType request, AnswerType answer);

Абстрактный класс MSMQUser, наследуемый классами MSMQServer и MSMQClient.

public abstract class MsmqUser { // использование восстанавливаемых сообщений private bool recoverable = false; public bool Recoverable

{ get { return recoverable; } set { recoverable = value; } } // объекты форматирования для посылки приема сообщений protected IMessageFormatter requestFormatter; protected IMessageFormatter answerFormatter; // public MsmqUser(QueueFormatter formatterType) { if (formatterType == QueueFormatter.Xml) { requestFormatter = new XmlMessageFormatter( new Type[]{typeof(RequestType)}); answerFormatter = new XmlMessageFormatter( new Type[]{typeof(AnswerType)}); } if (formatterType == QueueFormatter.Binary) { requestFormatter = new BinaryMessageFormatter(); answerFormatter = new BinaryMessageFormatter(); } } }

Класс общего вида, посылающий через MSMQ запросы и получающий ответы на них.

class MsmqClient<RequestType, AnswerType> : MsmqUser<RequestType, AnswerType>, IDisposable { // очереди для отсылки запросов и приема ответов private MessageQueue queueSend; private MessageQueue queueReceive; // список необслуженных запросов private Dictionary<String, RequestType> messages; public Dictionary<String, RequestType> Messages { get { return messages;} }

Page 89: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 5. MSMQ – промежуточная среда обмена сообщениями

84

// событие, вызываемое при приеме ответа public event ProcessAnswerEventHandler<RequestType, AnswerType> ProcessAnswer;

Конструктор, получающий имена очередей для посылки и приема сообщений.

public MsmqClient(String queueSendName, String queueReceiveName, QueueFormatter formatterType): base(formatterType) { // список отправленных сообщений без ответов messages = new Dictionary<String,RequestType>(); // создание очереди для посылки запросов, если она не существует queueSend = MsmqTools.CreateQueue(queueSendName, QueueType.Transactional); // создание очереди для приема ответов, если она нужна if (queueReceiveName != null) { queueReceive = MsmqTools.CreateQueue(queueReceiveName); queueReceive.Formatter = answerFormatter; // считывать из очереди свойство CorrelationId queueReceive.MessageReadPropertyFilter.CorrelationId = true; } else { queueReceive = null; } }

В методе Dispose происходит закрытие используемых очередей.

public void Dispose() { queueSend.Close(); queueSend.Dispose(); if (queueReceive != null) { queueReceive.Close(); queueReceive.Dispose(); } }

Функции BeginReceive и EndReceive начинают и прекращают прием ответов сервера,

изменяя обработчик события PeekComplete очереди ответов.

public void BeginReceive() { // установить обработчик на событие, возникающее при появлении // сообщения в очереди queueReceive.PeekCompleted += OnPeek; // начать отслеживание поступления сообщения в очередь queueReceive.BeginPeek(); }

Page 90: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 5. MSMQ – промежуточная среда обмена сообщениями

85

// прекратить прием ответов сервера public void EndReceive() { // отключить обработчик queueReceive.PeekCompleted -= OnPeek; }

Функция Send посылает в исходящую очередь запрос общего типа для его обработки

сервером. Для ответа на сообщение серверу следует использовать очередь, указанную в

поле ResponseQueue посылаемого сообщения.

public void Send(RequestType request) { // создание нового сообщения Message message = new Message(request, requestFormatter); message.ResponseQueue = queueReceive; // использование восстаналиваемых сообщений message.Recoverable = Recoverable; // послать сообщение; поскольку транзакция состоит из // единственной операции, вместо объекта-транзакции используется // значение MessageQueueTransactionType.Single queueSend.Send(message, MessageQueueTransactionType.Single); // поле message.Id устанавливается после посылки сообщения; // идентификатор сообщения связывается c отосланным запросом // в списке необслуженных запросов messages.Add(message.Id, request); }

Обработчик события очереди PeekComplete использует внутренние транзакции MSMQ.

В одну транзакцию входит операция чтения ответа из очереди и последующий вызов

события ProcessAnswer. Если в ходе обработки события возникло исключение, ответ

сервера останется в очереди ответов. Иначе сообщение удаляется из поддерживаемого

клиентом списка невыполненных запросов.

public void OnPeek(Object source, PeekCompletedEventArgs asyncResult) { // создание внутренней транзакции MSMQ MessageQueueTransaction transaction = new MessageQueueTransaction(); // начало транзакции transaction.Begin(); try { // прекратить ожидание сообщений в очереди queueReceive.EndPeek(asyncResult.AsyncResult); // получить сообщение из очереди в рамках транзакции Message message = queueReceive.Receive(transaction);

Page 91: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 5. MSMQ – промежуточная среда обмена сообщениями

86

// в поле CorrelationId должен быть идентификатор сообщения // с исходным запросом String messageId = message.CorrelationId; // есть ли такое сообщение в списке невыполненных запросов? if (messages.ContainsKey(messageId)) { if (message.Body is AnswerType) { // преобразовать тело сообщения к типу ответа // и вызвать событие по его обработке AnswerType answer = (AnswerType) message.Body; ProcessAnswer(this, messages[messageId], answer); }; messages.Remove(messageId); } // продолжить ожидать сообщения BeginReceive(); // успешное завершение транзакции transaction.Commit(); } catch (Exception e) {

// отмена транзакции transaction.Abort();

throw e; } } }

MSMQServer – класс общего вида, принимающий через MSMQ запросы и посылающий

ответы на них.

class MsmqServer<RequestType, AnswerType>: MsmqUser<RequestType, AnswerType>, IDisposable { // очередь приема запросов private MessageQueue queueReceive; // событие, вызываемое при приеме запроса public event ProcessRequestEventHandler<RequestType, AnswerType> ProcessMessage;

Конструктор класса проверяет наличие очереди.

public MsmqServer(String queueReceiveName, QueueFormatter formatterType): base(formatterType) { // создание очереди приема сообщений, если она не существует queueReceive = MsmqTools.CreateQueue(queueReceiveName, QueueType.Transactional); queueReceive.Formatter = requestFormatter; }

В методе Dispose происходит закрытие используемых очередей.

Page 92: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 5. MSMQ – промежуточная среда обмена сообщениями

87

public void Dispose() { queueReceive.Close(); queueReceive.Dispose(); }

Функции BeginReceive и EndReceive начинают и прекращают прием ответов сервера,

изменяя обработчик события PeekComplete очереди ответов.

// начать прием запросов от клиента public void BeginReceive() { queueReceive.PeekCompleted += OnPeek; queueReceive.BeginPeek(); } // прекратить прием запросов от клиента public void EndReceive() { queueReceive.PeekCompleted -= OnPeek; }

Метод OnPeek – обработчик события PeekCompleted очереди с запросами. В одну

транзакцию входит две операции с очередями – чтения запроса и отправка ответа на

него. Для обработки принятого сообщения и создания ответа на него вызывается

событие ProcessMessage. В поле ResponseQueue полученного сообщения содержится

ссылка на очередь, в которую следует отправить ответ на обработанный запрос.

// обработчки события PeekCompleted очереди с запосами public void OnPeek(Object source, PeekCompletedEventArgs asyncResult) { // создание внутренней транзакции MSMQ MessageQueueTransaction transaction = new MessageQueueTransaction(); // начало транзакции transaction.Begin(); try { queueReceive.EndPeek(asyncResult.AsyncResult); // прием cообщения в рамках транзакции Message message = queueReceive.Receive(transaction); // в поле ResponseQueue содержится ссылка на очередь, // куда следует послать ответ на запрос MessageQueue queueResponse = message.ResponseQueue; try { if (message.Body is RequestType) { RequestType request = (RequestType) message.Body; // вызвать событие обработки запроса AnswerType answer = ProcessMessage(this, request, queueResponse);

Page 93: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 5. MSMQ – промежуточная среда обмена сообщениями

88

if ((queueResponse != null) && (answer != null)) { Message answerMessage = new Message(answer, answerFormatter); answerMessage.Label = "Answer"; answerMessage.CorrelationId = message.Id; answerMessage.Recoverable = Recoverable; // послать собщение в рамках транзакции queueResponse.Send(answerMessage, transaction); } } } finally { if (queueResponse != null) { queueResponse.Close(); queueResponse.Dispose(); } }; // продолжить прием запросов BeginReceive(); // завершить транзакцию transaction.Commit(); } catch (Exception e) { // отменить транзакцию в случае ошибки Console.WriteLine(e); transaction.Abort(); throw e; } } }

Класс MsmqTools содержит вспомогательный статический метод для создания очереди

сообщений.

static class MsmqTools { static public MessageQueue CreateQueue(String queueName) { return CreateQueue(queueName, QueueType.Transactional); } // функция проверяет наличие очереди и создает ее при необходимости static public MessageQueue CreateQueue(String queueName, QueueType type) { MessageQueue messageQueue;

Page 94: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 5. MSMQ – промежуточная среда обмена сообщениями

89

// если это частная очередь удаленного компьютера, // то при попытке проверки ее наличие возникает исключение try { if (!MessageQueue.Exists(queueName)) { MessageQueue.Create(queueName, type == QueueType.Transactional); } } catch(Exception) { } MessageQueue messageQueue = new MessageQueue(queueName); return messageQueue; } } }

Следует отметить, что при работе с общими очередями можно обращаться к очереди по

ее пути, например следующим образом.

queueName = @"Server\PublicQueue";

При использовании частных очередей на удаленном компьютере в большинстве

случаев требуется применять прямое имя очереди.

queueName = @"Formatname:DIRECT=OS:Computer\Private$\PrivateName";

Имена используемых очередей следует хранить в конфигурационном файле

программы.

5.5. Выводы по использованию MSMQ

Промежуточная среда Microsoft Message Queuing обеспечивает асинхронный обмен

сообщениями и может быть использована программными компонентами

распределенной системы в одном из следующих случаях:

– необходимо организовать параллельную обработку заявок несколькими

компьютерами;

– одна компонента посылает другой запросы без получения ответов на них;

– взаимодействие компонент не должно быть синхронным;

– требуется интеграция с какими-либо другими независимыми системами,

которые могут использовать очереди сообщений (MSMQ или IBM MQ).

Page 95: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 5. MSMQ – промежуточная среда обмена сообщениями

90

Альтернативным способом использования MSMQ являются отложенные компоненты

(queued components) среды COM+, которые будут рассмотрены в разделе, посвященном

COM+. При использовании отложенных компонент MSMQ теряет одно из своих

достоинств – клиент должен иметь доступ к интерфейсу удаленной компоненты, как и

в случае использования любых других COM+ компонент. Кроме того, существует

возможность использовать MSMQ как канал в .NET Remoting, для чего необходимо

создать собственный канал, что будет проделано в соответствующей теме. Таким

образом, MSMQ является не только самостоятельной промежуточной средой, но и

может быть использовано другими промежуточными средами как асинхронный канал

передачи данных.

Page 96: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

91

Тема 6. Промежуточная среда COM+ и служба Enterprise Services

6.1. Введение в промежуточную среду COM+

COM+ – промежуточная среда для создания распределенных систем, действующих в

локальной сети. Она разрабатывается фирмой Microsoft с конца 90-х годов и впервые

появилась в составе операционной системы Microsoft Windows 2000. Основной целью

разработки среды COM+ было создание инфраструктуры для разработки

распределенных систем автоматизации предприятия. Основные достоинства среды

COM+:

– поддержка как синхронного, так и асинхронного взаимодействия программных

компонент;

– совместная работа с координатором распределенных транзакций (distributed

transactions coordinator, DTC);

– поддержка метода доступа единственного вызова с пулом объектов;

– использование для ограничения доступа к компоненте ролей (roles),

связываемых администратором системы с учетными записями пользователей.

Среда COM+ управляет ходом выполнения объектов COM+, являющимися

экземплярами так называемых компонент COM+. Набор связанных компонент COM+,

находящихся в одной динамической библиотеке, называется приложением COM+.

Приложение COM+ состоит из набора компонент и ролей для доступа к ним. Сведения

о зарегистрированных приложениях хранятся в каталоге COM+.

Приложения COM+ бывают двух видов: библиотечные и серверные. Экземпляры

компонент библиотечных приложений выполняются в том же процессе, что и

использующий их клиент, компоненты серверного – в отдельном потоке сервера,

возможно выполняющимся на удаленном компьютере. Только серверные приложения

могут использоваться удаленно путем регистрации в каталоге COM+ на компьютере

клиента посредника приложения COM+. После установки посредников использование

удаленных серверных компонент COM+ не отличается от использования локальных

Page 97: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

92

серверных компонент. Понятие посредник (proxy) используется в COM+ в двух

различных смыслах – запись в каталоге COM+, связанная с некоторым приложением

COM+ на удаленном компьютере, и в том же смысле, что и посредник при удаленном

вызове.

Поскольку библиотечные приложения COM+ не используют механизм удаленных

вызовов и могут использоваться только в адресном пространстве клиента, то их

рассмотрение не относится к данному курсу, и далее речь пойдет исключительно о

серверных приложениях COM+.

На рисунке рис. 6.1 показана архитектура среды COM+ при использовании серверных

приложений. Объекты COM+ являются экземплярами компонент COM+,

зарегистрированных в каталоге. Заглушка на стороне серверного процесса называется в

COM+ перехватчиком (interceptor).

Компьютер 1

Клиентский процесс

Компьютер 2

СредаCOM+

СредаCOM+Серверный

процесс

Объект COM+

КаталогCOM+

ПерехватчикПосредник Посредник

RPC

Перехватчик

ОбъектCOM+

КаталогCOM+

RPC

Вызывающая функция

Объект COM+

Перехватчик

Рисунок 6.1. Архитектура среды COM+

Каталог COM+ каждого компьютера содержит список зарегистрированных на

компьютере локальных приложений COM+, а также список установленных

Page 98: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

93

посредников для связи с приложениями удаленных компьютеров. Каталог устроен

иерархически, в виде дерева. Например, узел с описанием приложения COM+ содержит

узел со списком входящих в него компонент COM+ и узел со списком ролей

приложений. Управление каталогом COM+ происходит при помощи оснастки

comexp.msc или программно, используя классы библиотеки comadmin.dll. Подробнее

состав каталога описан в приложении I.

Поскольку среда COM+ реализует свое собственное управлением исполняемым внутри

нее кодом, то существует понятие контекста COM+, который представляет собой

окружение объекта COM+. Контекст закрепляется за объектом в момент его активации

и сохраняется до его деактивации. При создании контекста учитываются атрибуты

необходимости транзакции и синхронизации, установленные в активируемой

компоненте COM+, а так же текущий контекст в момент активации объекта.

6.2. Сервисы COM+

Целью создания промежуточной среды COM+ являлось предоставление компонентам

COM+ набора сервисов, облегчающему создание надежной и масштабируемой

распределенной системы. Начиная с Windows 2003/XP SP2, часть этих сервисов может

быть доступна и без создания компонент COM+, путем использования так называемых

сервисов без компонент.

Синхронизация

Среда COM+ позволяет исключить проблемы с синхронизацией при обслуживании

запроса клиента путем так называемых активностей. Активность начинается в момент

создания клиентом COM+ объекта и заканчивается при его удалении. В течении

активности клиент вызывает методы компоненты COM+, в ходе которых она может

создавать другие объекты COM+, в том числе расположенные на удаленных

компьютерах, и вызывать их методы. При этом COM+ гарантирует, что в течение

одной активности в каждый момент выполняется метод только одного COM+ объекта

из всех участвующих в активности. Таким образом, активность представляет собой

некий логический поток. Объекты COM+ могут как участвовать в активности

(требовать синхронизации), так и не участвовать, в зависимости от атрибутов

Page 99: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

94

соответствующей им компоненты COM+ и текущего контекста при их создании, как

показано в таблице 6.1.

Таблица 6.1 Синхронизация создаваемого объекта COM+

Настройка синхронизации компоненты COM+

Создатель объекта участвует в активности

Создатель объекта не участвует в активности

Не поддерживается (Not Supported)

Вне активности Вне активности

Поддерживается (Supported)

Активность создателя Вне активности

Требуется (Required)

Активность создателя Новая активность

Требуется новая (Requires new)

Новая активность Новая активность

Следует учитывать, что настройки участия в транзакции компоненты влияют на ее

настройки синхронизации, о чем будет сказано далее.

Балансировка нагрузки

COM+ поддерживает динамическую балансировку нагрузки. Для этого необходимо

создать кластер COM+ на базе серверной версии операционной системы Microsoft

Windows (Windows 2000 Advanced Server и последующих). Кластер COM+ состоит из

нескольких серверов и распределяющим между ними запросы маршрутизатором

COM+. Для повышения надежность такой системы могут применяться способы

быстрой замены маршрутизатора при его выходе из строя на базе Windows Clustering

Services. Таким образом, среда COM+ позволяет при наличии необходимости

достаточных финансовых ресурсов создать хорошо масштабируемую систему без

уникальной точки сбоя.

Just-in-time-активация и пул объектов

Среда COM+ поддерживает два вида активации объектов – активация по требованию

клиента и активация на время единственного вызова (называемая в среде COM+

JIT-активацией) с возможностью использования пула объектов. Поскольку

Page 100: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

95

распределенные транзакции поддерживаются только для второго типа активации, в

дальнейшем будет рассматриваться только он.

Распределенные транзакции

Одним из основных достоинств среды COM+ является поддержка распределенных

транзакций на базе координатора распределенных транзакций. Настройки транзакции

компоненты COM+ влияют на настройки синхронизации и активации. Любая

включенная настройка транзакций, отличная от Not supported, требует использования

JIT-активации и ограничивает выбор настройки синхронизации не более чем

вариантами Required и Requires new. При активации объекта среда COM+ определяет

необходимость использования транзакций в соответствии с таблицей 6.2

Таблица 6.2 Участие объекта COM+ в транзакции

Настройка транзакции компоненты COM+

Создатель объекта участвует в транзакции

Создатель объекта не участвует в транзакции

Не поддерживается (Not Supported)

Вне транзакции Вне транзакции

Поддерживается (Supported)

Транзакция создателя Вне транзакции

Требуется (Required)

Транзакция создателя Новая транзакция

Требуется новая (Requires new)

Новая транзакция Новая транзакция

Если при создании объекта обнаружена потребность в создании новой транзакции,

среда COM+ создает ее с помощью координатора транзакций и данный объект

считается корнем транзакции (рис. 6.2). Поскольку требующие транзакцию объекты

используют активацию одного вызова, то транзакция не может существовать дольше,

чем один вызов метода корневого объекта клиентом COM+. В транзакции могут

участвовать службы, имеющие свой менеджер ресурсов, в частности MSMQ и MS SQL.

Для многих ресурсов при необходимости можно создать свой компенсирующий

менеджер ресурсов.

Page 101: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

96

Участникитранзакции

БД

Кординатортранзакций

КлиентКонтекст COM+ Корневой объект

транзакцииID транзакции

Объект, участвующий в транзакцииКонтекст COM+ID транзакции

Компенсирующийменеджер ресурсов

Рисунок 6.2. Транзакция COM+

Каждый из участвующих в транзакции объектов должен в конце выполнения своего

метода сообщить об успешности транзакции или ее неудаче. В случае, если все

объекты объявили об успешности транзакции, служба COM+ оповещает все

участвующие в транзакции внешние службы о необходимости сделать произведенные в

рамках транзакции изменения постоянными (рис. 6.3).

Сохранить

изменения

БДКлиент Корневой объекттранзакции

Объект, участвующий в транзакции

Успех

Компенсирующийменеджер ресурсов

Координатортранзакций

Сохранитьизменения

Успех

Рисунок 6.3. Успешное завершение транзакции COM+

Page 102: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

97

Ожидающие компоненты

Хотя среда MSMQ может использоваться в рамках транзакции COM+, это приводит к

одновременному использованию двух технологий удаленного взаимодействия.

Вероятно часто было бы удобно скрывать использование MSMQ путем создания

компонент COM+, поддерживающих асинхронные коммуникации. Поэтому для

реализации асинхронного удаленного вызова в промежуточной среде COM+

существуют так называемые ожидающие компоненты (queued componenets), которые

прозрачно используют MSMQ. Использование такой компоненты подобно

асинхронному удаленному вызову (рис. 6.4).

Компьютерс отложеннойкомпонентой

Компьютер клиентаотложенной компоненты

КаталогCOM+

Клиент

Протоколист

MSMQ

СлушательПомощник слушателя

Исполнитель

Интерфейскомпоненты

Отложеннаякомпонента COM+

Интерфейскомпоненты

Рисунок 6.4. Использование ожидающих компонент COM+

1. При начале использования ожидающей компоненты на стороне клиента создается

посредник, называемый протоколистом (recorder), сохраняющий историю вызовов

компоненты.

Page 103: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

98

2. После завершения использования компоненты, если не произошло отката

транзакции, протоколист формирует сообщение MSMQ со всеми вызовами

компоненты.

3. На стороне сервера сообщение MSMQ ожидается слушателем (listener), который не

является COM+ компонентой. При появлении сообщения в очереди он создает

специальную COM+ компоненту, называемую помощником слушателя (listener helper),

которая считывает сообщение из очереди.

4. После считывания сообщения помощник слушателя создает исполнитель (player),

который и создает сам экземпляр отложенной компоненты, воспроизводя затем

последовательность ее вызовов в рамках той же транзакции. С точки зрения

отложенной компоненты исполнитель является обычным клиентом.

Аналогичным образом происходит получение результата вызова удаленной

компоненты: на сервере создается протоколист, а на клиенте – слушатель и

исполнитель. Однако в этом случае до начала использования удаленной компоненты

клиенту следует создать вызываемый объект (call-back object), который будет

принимать ответ от сервера через исполнителя, и передать ссылку на такой объект

(точнее, на его исполнителя), серверу (рис. 6.5).

Компьютерс отложенной компонентой

Компьютер клиентаотложенной компоненты

MSM

Q

Вызываемыйобъект

Клиент Протоколист

Исполнитель Протоколист

Исполнитель

Отлож

енная

комп

онента

Рисунок 6.5. Взаимодействие с отложенной компоненты

Слабо связанные события

Среда COM+ позволяет компонентам подписаться на события, создаваемые

какими-либо объектами COM+ (издателями событий). При этом подписчики и издатели

Page 104: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

99

могут не знать о существовании друг друга, но должны знать о существовании общего

интерфейса COM+, который используется для публикации событий. Наравне с

отложенными компонентами, слабо связанные события предоставляют второй вариант

реализации асинхронного обмена данными в среде COM+.

Обеспечение безопасности

Определение политики управления доступом для компонент COM+ осуществляется на

основе ролей безопасности. Компоненты COM+ должны иметь возможность

использовать встроенные в операционную систему механизмы обеспечения

безопасности. Для абстрагирования от конкретных пользователей операционной

системы в COM+ введено понятие ролей безопасности.

Роль безопасности – это категории пользователей приложения COM+, имеющих

определенные права на доступ к компонентам данного приложения, их интерфейсам и

методам. Разработчик компоненты COM+ задает роли как некоторые символьные

значения и определяет уровни доступа для всех использующихся ролей. При

разворачивании приложения ролям ставятся в соответствие учетные записи

пользователей и групп Microsoft Windows. Следует отметить, что роли COM+ не

связаны напрямую с ролями CLI, поскольку COM+ существует независимо от .NET

Framework.

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

безопасности приложения. В идеале названия ролей должны соответствовать

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

оснастки служб компонентов можно получить описание каждой роли, в котором могут

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

6.3. Использование среды COM+ в приложениях .NET Framework

Взаимодействие среды COM+ и среды CLR

Среда COM+ была создана до технологии .NET, поэтому она работает с

неуправляемым кодом и не является носителем исполняемой среды CLR. Для

Page 105: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

100

использовании сервисов COM+ из .NET Framework необходимо получить возможность

использовать управляемый код в контексте COM+. Для решения данной проблемы

была создана достаточно сложная схема взаимодействия сред CLR и COM+,

основанная на понятии компоненты, использующей сервисы COM+ (serviced

component), называемой далее обслуживаемой (средой COM+) компонентой. Такая

компонента состоит из объекта управляемого кода, принадлежащему наследованному

от System.EnterpriseServices.ServicedComponent классу. Благодаря наследованию от

ServicedComponent при исполнении методов этого объекта имеется доступ к контексту

COM+. Использование таких компонент упрощенно показано на рис. 6.6.

Процесс клиентаУправляемый код

Процесс сервераНеуправляемый код

Управляемый код

ПосредникКлиент

Посредниксервиснойкомпоненты

.NETRemoting

Экземплярсервиснойкомпоненты

COM+

Рисунок 6.6. Взаимодействие COM+ и CLR при использовании серверных

приложений

Маршализация параметров методов обслуживаемой компоненты при использовании

происходит под управлением службы .NET Remoting (с использованием класса

форматирования BinaryFormatter), а среда COM+ используется только для реализации

своих сервисов.

Page 106: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

101

Как видно из рисунка 6.6, обслуживаемая компонента .NET Framework не является,

строго говоря, компонентой COM+. Аналогично и промежуточная среда .NET

Enterpsise Services является не новым названием среды COM+, а средством

использования сервисов COM+ в управляемом коде. Хотя часто можно упрощенно

считать, что обслуживаемые компоненты – это компоненты COM+ на управляемом

коде, но при разработке обслуживаемых компонент существуют случаи, когда

желательно понимать взаимосвязь CLR и COM+. Например, если при вызове метода

управляемой компоненты происходит ошибка в момент выполнения неуправляемого

кода среды COM+, то полученная от посредником COM+ информация преобразуется в

исключение типа System.Runtime.InteropServices.COMException. В качестве текста

сообщения в этом исключении фигурируют ошибки среды COM+, достаточно

малопонятные с точки зрения управляемого кода, например такие.

Unhandled Exception: System.Runtime.InteropServices.COMException (0x8000FFFF): Catastrophic failure (Exception from HRESULT: 0x8000FFFF (E_UNEXPECTED)) Unhandled Exception: System.Runtime.InteropServices.COMException (0x8004D082): Exception from HRESULT: 0x8004D082

При возникновении исключения в управляемом коде обслуживаемой компоненты

происходит обычное исключение .NET, которое, тем не менее, должно уметь

маршализироваться по значению с помощью .NET Remoting между доменами

приложений, иначе вместо него к клиенту придет исключение от инфраструктуры .NET

Remoting о невозможности сериализации выброшенного исключения.

Создание обслуживаемых компонент

Обслуживаемая компонента .NET Framework является объектом класса, наследованным

от System.EnterpriseServices.ServicedComponent, и отвечающим следующим

требованиям:

– класс имеет спецификатор public;

– класс содержит публичный конструктором без параметров;

– класс не является абстрактными, статическим, или классом общего вида.

Статические методы в классе компоненты возможны, но они выполняются в контексте

клиента, и имеют доступа к контексту COM+ клиента, если таковой существует.

Page 107: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

102

Класс сервисной компоненты должен быть объявлен в сборке, которая может быть

зарегистрирована в качестве приложения COM+. Такая сборка не должна иметь классов

общего вида со спецификатором public, и должна быть подписана.

Класс обслуживаемой компоненты может иметь атрибуты из пространства имен

System.EnterpriseServices, связанные с транзакцией (TransactionAttribute), активацией

(JustInTimeActivationAttribute и ObjectPoolingAttribute) и синхронизацией

(SynchronizationAttribute). Как упоминалось ранее, поддержка транзакции

автоматически означает JIT-активацию и участие в активности, тем не менее следует

перечислять атрибут JIT-активации явно.

Рассмотрим код простейшей обслуживаемой компоненты.

// Файл SampleComponent.cs using System; using System.EnterpriseServices; [assembly: ApplicationAccessControl(false)] [assembly: ApplicationName("Serviced Component Example 01")] [assembly: Description("Sample of .NET serviced component.")] [assembly: ApplicationActivation(ActivationOption.Server)]

Эти атрибуты задают свойства сборки, которая затем будет зарегистрирована в

качестве приложения COM+. Атрибут ApplicationActivation позволяет выбрать

серверный или библиотечный тип приложения.

namespace ServicedComponentSample { [JustInTimeActivation] [Transaction(TransactionOption.Required)] [ObjectPooling(Enabled=true, MinPoolSize=5, MaxPoolSize=10)] public class SampleComponent: ServicedComponent {

Объявленный выше класс компоненты, как видно из атрибутов, требует наличия

транзакции и использует JIT-активацию с пулом объектов

public SampleComponent() { }

Для использования в пуле объект должен быть возвращен в свое первоначальное

состояние (деактивизирован). Для поддержки режима активации одного вызова

компонента не должна иметь состояния, сохраняемого между вызовами ее методов

Page 108: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

103

клиентом, и таким образом часто она не требует каких-либо действий для перевода в

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

Activate и Deactivate, вызываемые при операциях активации и деактивации, и

производить в них сброс состояния объекта.

protected override void Activate() { } protected override void Deactivate() { // Здесь при необходимости можно очистить // состояние объекта и освободить ресурсы }

Метод CanBePooled возвращает true, если объект может быть возвращен в пул объектов

после деактивации, иначе он уничтожается.

protected override bool CanBePooled() { // Подтверждает возможность помещения объекта в пул return true; }

Атрибут System.EnterpriseServices.AutoCompleteAttribute применяется для

автоматического участия методов в транзакции. Если метод завершается нормально, то

считается, что он подтвердил успешное завершение транзакции, при возникновении же

в методе не пойманного исключения транзакция будет отменена.

[AutoComplete] public void Do() { } } } // Файл SampleComponent.cs

Альтернативный AutoComplete вариант использования транзакций заключается в

вызове статических методов класса ContextUtil. Этот класс используется для доступа и

изменения контекста COM+, связанного с обслуживаемой компонентой. Метод

ContextUtil.SetComplete сообщает COM+ об успешном завершении метода, а

ContextUtil.SetAbort предопределяет отмену транзакции. Используются они обычно

следующим образом.

Page 109: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

104

try { ... ContextUtil.SetComplete(); } catch() { ContextUtil.SetAbort(); }

Постоянное использование методов ContextUtil приводит к коду, который будет сложно

перенести из компоненты COM+ в класс, выполняемый вне промежуточной среды

COM+. Для решения этой проблемы следует либо использовать методы класса,

оборачивающего методы ContextUtil, либо, в крайнем случае, атрибут AutoComplete,

который игнорируется для обычных классов.

Регистрация обслуживаемых компонент

Сборка, содержащая описание одного или нескольких классов компонент, должна быть

зарегистрирована как приложение COM+ в каталоге COM+. Для этого сборка должна

быть подписанной (strong-named assembly). Неподписанные сборки идентифицируются

своим именем, версией и информацией о типе культуры. Подписанная сборка содержит

также открытый ключ и цифровую подпись, созданную закрытым ключом. Таким

образом, при наличии открытого ключа можно проверить неизменность кода сборки.

Для генерации пары из закрытого и открытого ключа служит утилита sn.exe из .NET

Framework SDK. Для создания сборки из приведенного выше файла

SampleComponent.cs следует выполнить (или внести в make-файл) следующие

комманды.

sn -k SampleComponent.snk csc /target:library /r:System.EnterpriseServices.dll SampleComponent.cs /keyfile:SampleComponent.snk

После успешного создания подписанной сборки остается зарегистрировать ее в

каталоге COM+. В .NET Framework существуют три способа регистрации

обслуживаемых компонент:

– с использованием класса System.EnterpriseServices.RegistrationHelper;

– c использованием утилиты regsvcs.exe;

– автоматическая регистрация сборки в момент создания экземпляра

обслуживаемой компоненты.

Page 110: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

105

Обычно рекомендуется применять первые два способа. Для регистрации созданной

сборки выполняется следующая команда.

regsvcs SampleComponent.dll

Для удаления сборки из каталога COM+ выполняется аналогичная команда.

regsvcs /u SampleComponent.dll

В качестве примера использования созданной компоненты рассмотрим следующий

файл.

// Файл SampleClient.cs using System; using ServicedComponentSample; class Test { static public void Main() { using(SampleComponent com = new SampleComponent()) { com.Do(); } } }

// SampleClient.cs

Использование оператора using приводит к вызову метода Dispose для объекта. Это

необходимо для своевременной очистки используемых при создании обслуживаемой

компоненты ресурсов COM+. Для компиляции данного файла следует следующую

команду.

csc /target:exe /r:SampleComponent.dll SampleClient.cs

Для связывания этого приложения с компонентой на удаленном компьютере следует

зарегистрировать на нем сборку SampleComponent.dll. Затем, пользуясь оснасткой

comexp.msc на удаленном компьютере следует посредника приложения COM+, и

установить посредника на компьютере клиента с исполняемым файлом

SampleClient.exe. Следует отметить, что сборка может быть либо установлена в каталог

COM+ в качестве локального приложения, либо как посредник приложения на

удаленном компьютере, но не то и другое одновременно.

Page 111: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

106

Использование исключений в обслуживаемых компонентах

Использование исключений в распределенных системах имеет свои особенности. Если

исключение выбрасывается в обслуживаемой компоненте, то оно в общем случае

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

сериализации и десериализации в рамках среды .NET Remoting с использованием

класса BinaryFormatter. Поэтому все исключения, используемые обслуживаемыми

компонентами, должны иметь возможность сериализации наследниками

System.Runtime.Serialization.Formatter. Все исключения из библиотеки FCL,

наследованные от System.ApplicationException и System.SystemException, имеют такую

возможность. Ниже приведен пример создания собственного сериализуемого

исключения. Класс исключения имеет конструктор с параметрами, описанными в теме

об использовании форматеров SoapFormatter и BinaryFormatter. К счастью,

разработчику достаточно вызвать аналогичный конструктор базового класса

System.SystemException, который реализует все необходимые действия.

// Файл ComException.cs using System; using System.EnterpriseServices; using System.Runtime.Serialization; [assembly: ApplicationActivation(ActivationOption.Server)] [assembly: ApplicationAccessControl(false)] [Serializable] public class CustomException: System.ApplicationException { public CustomException(SerializationInfo info, StreamingContext context): base(info, context) { } public CustomException(string message): base(message) { } } public class ComSample: ServicedComponent { public ComSample() { } public void Process() { throw new CustomException("случилась неприятность"); } }

Page 112: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

107

class MainApp { static public void Main() { ComSample com = new ComSample(); try { com.Process(); } catch(CustomException e) { Console.WriteLine("Исключение: {0}", e.Message); } finally { com.Dispose(); } } } // Файл ComException.cs

Для создания приложения и его регистрации можно выполнить следующие команды.

sn -k ComException.snk csc ComException.cs /keyfile:ComException.snk /r:System.EnterpriseServices.dll regsvcs ComException.exe

Создание компенсирующего менеджера ресурсов

Компенсирующий менеджер ресурсов позволяет использовать в транзакциях среды

COM+ какие-либо ресурсы, которые не имеют прямой поддержки транзакций COM+.

Этот механизм включает следующие классы из пространства имен

System.EnterpriseServices.CompensatingResourceManager:

– журнал операций (log), заполняемый операциями над ресурсом;

– секретарь (класс Clerk), ведущий журнал операций;

– компенсатор (наследника класса Compensator), который восстанавливает

первоначальное состояние ресурса в случае отката транзакции и вносит

изменения в ресурс при успехе транзакции.

Сам менеджер ресурсов должен решить задачу изоляции ресурса в рамках транзакции и

ведения журнала операций. При успехе транзакции компенсатор вносит постоянные

изменения или отменяет их в соответствии с журналом операций. Следует отметить,

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

вероятно нарушить требования изоляции транзакции, поскольку могут быть видимыми

для внешних по отношению к транзакции объектов. С другой стороны, объекты внутри

Page 113: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

108

транзакции должны наблюдать происходящие с ресурсом изменения до успешного

завершения транзакции.

В качестве примера менеджера ресурсов рассмотрим работу с файлами небольшого

размера. Менеджер ресурсов предоставляет следующие сервисы:

– открытие документа на чтение;

– открытие документа на перезапись;

– закрыть файл с сохранением изменений.

С целью изоляции транзакций запись в файл происходит в момент успешного

завершения транзакции. До этого момента данные хранятся в памяти. Такое решение

безусловно не является эффективным с точки зрения траты ресурсов решение, оно

используется в качестве примера. При чтении в рамках транзакции документа, уже

ранее измененного в этой же транзакции, вместо чтения из файла происходит чтение

последней относящийся к этому файлу записи из журнала операций. Журнал операций

хранит пары из имени файла (в нижнем регистре) и содержимого файла.

Пример компилируется и запускается make-файлом, который можно передать как

параметр входящей в состав .NET SDK утилите nmake.exe. Этот файл имеет следующее

содержание.

# Файл: makefile all: CrmSample.exe # сборка должна быть подписана CrmSample.key: sn -k CrmSample.key CrmSample.exe: CrmSample.cs CrmSample.Key csc /r:System.EnterpriseServices.dll CrmSample.cs /keyfile:CrmSample.key install: # установить приложение COM+ regsvcs CrmSample.exe uninstall: regsvcs –u CrmSample.exe

Поскольку для регистрации сборки как приложения COM+необходимо, чтобы она была

подписана, то вызовом утилиты sn.exe из состава .NET SKD создается пара ключей.

Затем компилятор языка C# csc.exe создает сборку, используя этот файл ключей. При

команде nmake install сборка устанавливается в качестве приложения COM+ вызовом

утилиты regsvcs.exe Microsoft Windows.

Далее будет рассмотрено содержание файла CrmSample.cs.

Page 114: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

109

// Файл CrmSample.cs using System; using System.IO; using System.Collections.Generic; using System.EnterpriseServices; using System.EnterpriseServices.CompensatingResourceManager; [assembly: ApplicationActivation(ActivationOption.Server)] [assembly: ApplicationAccessControl(false)] [assembly: ApplicationCrmEnabled] [assembly: ApplicationName("Seva CRM")] [assembly: Description("Пример на использование CRM")]

Данные атрибуты сборки из пространства имен System.EnterpriseServices управляют

параметрами создаваемого приложения COM+:

– ApplicationActivation – задает тип приложения COM+ (серверный или

библиотечный);

– ApplicationCrmEnabled – необходим для использования CRM в приложении

COM+;

– ApplicationAccessControl – управляет контролем доступа к приложению, в

данном примере – отключен;

– Атрибут System.ComponentModel.DescriptionAttribute задает описание сборки.

Класс StreamLog содержит статический метод для записи в файл буфера, вызываемый

при завершении транзакции.

public static class StreamLog { public static void Save(LogRecord log) { if (log.Record is object[]) { object[] record = (object[])log.Record; string fileName = (string) record[0]; byte[] buffer = (byte[]) record[1]; using (FileStream file = new FileStream(fileName, FileMode.Create, FileAccess.Write)) { file.Write(buffer, 0, buffer.Length); } } } // Save() } // StreamLog

Класс StreamCache помогает организовать кеширование содержимого файлов,

использующихся в транзакции.

Page 115: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

110

public class StreamCache { private string fileName; private MemoryStream stream; public MemoryStream Stream { get { return stream;} } public string FileName { get { return fileName;} } public StreamCache(string streamFileName) { fileName = streamFileName; stream = new MemoryStream(); }

Метод Reopen при необходимости открывает повторно закрытый поток. Поскольку

такая операция не поддерживается классом MemoryStream, то сначала в массив

записывается все содержимое потока, затем создается новый поток, куда записываются

сохраненные данные.

public void Reopen() { if (!stream.CanRead) { stream.Close(); byte[] buffer = stream.ToArray(); stream.Dispose(); stream = new MemoryStream(); stream.Write(buffer, 0, buffer.Length); stream.Seek(0, SeekOrigin.Begin); } } // Reopen()

При открытии файла для чтения вызывается метод Read, считывающий все содержимое

файла в поток типа MemoryStream.

public void Read() { byte[] buffer = new byte[32*1024]; using (Stream inputStream = new FileStream(fileName, FileMode.Open, FileAccess.Read)) {

Page 116: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

111

while (true) { int read = inputStream.Read(buffer, 0, buffer.Length); if (read <= 0) { break; } Stream.Write(buffer, 0, read); } } } // Read() } // StreamCache

Класс StreamCrm является менеджером ресурсов, используемым объектами COM+. Он

является COM+ объектом, поэтому несколько участвующих в транзакции компонент

COM+ могут работать с одним менеджером ресурсов данного типа. Менеджер

содержит кеш для реализации отложенной до завершения транзакции записи в файл.

public class StreamCrm: ServicedComponent { private Dictionary<string, StreamCache> cache; public StreamCrm() { cache = new Dictionary<string, StreamCache>(); }

Метод StreamCrm.CheckCache проверяет, есть ли в кеше информация о данном файле.

При отсутствии ее в кеше в случае открытия файла на чтение происходит считывания

всего содержимого файла в кеш, в противном случае связанный с файлом поток

открывается повторно вызовом метода StreamCache.Reopen.

private StreamCache CheckCache(string fileName, bool read) { StreamCache streamCache; string key = Path.GetFullPath(fileName).ToLower(); if (!cache.ContainsKey(key)) { streamCache = new StreamCache(fileName); cache.Add(key, streamCache); if (read) { streamCache.Read(); }; } else { streamCache = cache[key]; streamCache.Reopen(); }; return streamCache; }

Page 117: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

112

Метод StreamCrm.ReadFromFile вызывается клиентом, желающим читать из файла,

метод StreamCrm.WriteToFile – желающим перезаписать файл.

public MemoryStream ReadFromFile(string fileName) { return CheckCache(fileName, true).Stream; } public MemoryStream WriteToFile(String fileName) { return CheckCache(fileName, false).Stream; }

Метод StreamCrm.Flush прекращает работу с потоком, открытым для записи и

сохраняет сделанные изменения в записи для журнала секретаря, которую возвращает в

качестве своего результата.

public object[] Flush(String fileName) { StreamCache streamCache = CheckCache(fileName, false); streamCache.Stream.Close(); object[] record = new object[2]; record[0] = fileName; record[1] = streamCache.Stream.ToArray(); return record; }

Статический метод StreamCrm.CreateClerk создает секретаря для ведения журнала

операций над ресурсом. Вызов статического метода не приводит к удаленному вызову

и смене контекста.

public static Clerk CreateClerk() { return new Clerk(typeof(StreamCompensator), "Compensator", CompensatorOptions.AllPhases); } }

Класс StreamCompensator наследуется от класса Compensator и служит для сохранения

результатов транзакции в случае успеха или возврате данных в первоначальное

состояние в случае отката транзакции.

public class StreamCompensator : System.EnterpriseServices.CompensatingResourceManager.Compensator { private bool prepared = false;

Page 118: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

113

Методы c суффиксом Prepare вызываются для проверки записей в журнале перед

завершением или откатом транзакции. Метод PrepareRecord должен вернуть false, если

запись журнала должна быть использована. Поскольку журнал записей не

контролирует типы записей, это должен сделать метод PrepareRecord.

public override void BeginPrepare () { } public override bool PrepareRecord(LogRecord log) { prepared = false; if (!(log.Record is object[])) return true; object[] record = log.Record as object[]; if (record.Length != 2) return true; if (!(record[0] is string) || !(record[1] is byte[])) return true; prepared = true; return false; }

Метод EndPrepare возвращает сохраненный в поле объекта результат проверки записи.

Если он возвращает true, то возможно успешное завершение транзакции.

public override bool EndPrepare () { return prepared; }

Методы c суффиксом Commit вызываются для сохранения результата транзакции. Если

метод CommitRecord возвращает истинное значение, то запись можно исключить из

журнала операций.

public override void BeginCommit (bool commit) { } public override bool CommitRecord (LogRecord log) { StreamLog.Save(log); return true; } public override void EndCommit () { }

Группа методов с суффиксом Abort служит для возвращения ресурсу первоначального

состояния при откате транзакции. Поскольку созданный менеджер ресурсов изменяет

Page 119: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

114

состояние файлов меняется только при успешном завершении транзакции, то эти

методы не содержат никаких действий.

public override void BeginAbort (bool abort) { } public override bool AbortRecord (LogRecord log) { return true; } public override void EndAbort () { } } // StreamCrm

Ниже приведен пример двух классов COM+, которые используют созданный менеджер

ресурсов. Напомним, что для регистрации в качестве COM+ компоненты классы

должен иметь публичный конструктор без параметров. Атрибут TransactionAttribute

управляет использованием транзакций COM+. Класс SampleCrmClient2 содержит метод

ReadLine, читающий из файла строчку с использованием созданного менеджера

ресурсов.

[Transaction(TransactionOption.Required)] public class SampleCrmClient2: ServicedComponent { public SampleCrmClient2() { } public string ReadLine(StreamCrm crm, string fileName) { using (StreamReader reader = new StreamReader(crm.ReadFromFile(fileName))) { return line = reader.ReadLine(); } } }

Метод SampleCrmClient.DoSomeWork демонстрирует использование CRM. Смена

текущей директории вызвана тем, что по умолчанию для серверных компонент COM+

текущей является директория %systemroot%\system32.

Page 120: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

115

[Transaction(TransactionOption.RequiresNew)] public class SampleCrmClient1: ServicedComponent { public SampleCrmClient1() { } public void DoSomeWork(string dir) { const string fileName1 = "sample1.txt"; const string fileName2 = "sample2.txt"; Environment.CurrentDirectory = dir; StreamCrm crm = new StreamCrm(); Clerk clerk = StreamCrm.CreateClerk();

Метод производит действия с двумя файлами. Сначала в поток, связанный с первым из

двух файлов, записывается некоторая строка. Собственно запись в файл в этот момент

не происходит, записанные данные запоминаются в кеше менеджера ресурсов после

вызова метода StreamCrm.Flush. Этот метод возвращает запись, которая помещается в

журнал секретаря и будет использована при завершении транзакции.

using (StreamWriter writer = new StreamWriter(crm.WriteToFile(fileName1))) { writer.Write(Environment.CurrentDirectory); } // добавление в журнал записи clerk.WriteLogRecord(crm.Flush(fileName1));

Затем из этого файла строка считывается другим объектом COM+ класса

SampleCrmClient2. Таким образом, если объекты в пределах одной транзакции будут

обращаться к файлам при помощи одного экземпляра менеджера ресурсов, то

изменения, внесенные одним объектом, будут видны другим объектам транзакции, но

не видны снаружи до ее завершения.

String tempString = ""; using (SampleCrmClient2 client2 = new SampleCrmClient2()) { tempString = client2.ReadLine(crm, Path.GetFullPath(fileName1)); }

Считанные объектом данные записываются во второй файл.

using (StreamWriter writer = new StreamWriter(crm.WriteToFile(fileName2))) { writer.WriteLine(String.Format("Считано из файла [{0}]:", fileName1)); writer.WriteLine(tempString); }

Page 121: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

116

// добавление в журнал записи clerk.WriteLogRecord(crm.Flush(fileName2)); // успешное завершение транзакциии, сохранение изменений в файлах ContextUtil.SetComplete(); } }

Класс CrmTest содержит метод Main. Вместо конструкции try .. finally … Dispose в C#

следует использовать оператор using с тождественным результатом. Однако в данном

примере хотелось бы показать, что при использовании объектов среды

EnterpriseServices/COM+ для них следует вызывать Dispose в явном или неявном (через

оператор using) виде.

class CrmTest { public static void Main() { SampleCrmClient1 client1 = new SampleCrmClient1(); try { client1.DoSomeWork(Environment.CurrentDirectory); } finally { client1.Dispose(); } } }

Кроме работы с компенсирующим менеджером ресурсов, на данном примере хотелось

бы показать, что хотя сборка мусора упрощает работу программиста, но при

использовании объектов с интерфейсом IDisposable всегда следует вызывать их метод

Dispose (явно или неявно) для своевременного освобождения ресурсов.

Использование слабо связанных событий

Для использования слабо связанных событий необходимо (рис. 6.7):

– создать интерфейс, описывающий методы события;

– создать так называемый класс события (event class), который является особой

обслуживаемой компонентой, реализующей указанный интерфейс;

– создать реализующую этот же интерфейс обслуживаемую компоненту и

подписать ее на событие; таких сервисных компонент можно создать несколько

и они могут входить в состав различных приложений COM+;

Page 122: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

117

– написать в издателе код создания события, заключающийся в создании

экземпляра класса события и вызова его методов.

Среда COM+

Подписчиксобытия

Объект события

Издательсобытия

Интерфейс

Подписка вкаталоге COM+

ПодписчиксобытияПодписчикисобытия

Интерфейс

Событие (вызов метода)

Рисунок 6.7. Использование слабо связанных событий

Сначала создается интерфейс, описывающий один или несколько методов-событий.

Поскольку для подписки на событие необходимо будет использовать идентификаторы

класса события и интерфейса, то они описаны как константы.

// LceEvents.cs using System; using System.Runtime.InteropServices; using System.EnterpriseServices; public static class EventsGuids { public const string interfaceId = "A5105B2C-40BF-46C6-B19C-4286A423DBF9"; public const string eventClassId = "DF64D391-CCE9-4FC6-B5F2-3F4DE3FA48C2"; } [Guid(EventsGuids.interfaceId)] public interface ILceMessage { void TriggerEvent(string message); } // LceEvents.cs

Далее описано приложение, являющееся издателем события.

Page 123: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

118

// LcePublisher.cs using System; using System.Threading; using System.Runtime.InteropServices; using System.EnterpriseServices; [assembly: ApplicationName("LCE publisher")] [assembly: ApplicationActivation(ActivationOption.Server)] [assembly: ApplicationAccessControl(false)]

Необходимо описать класс события, используя атрибут EventClassAttribute и

описанный ранее уникальный идентификатор. Методы данного класса не содержат

кода, класс события нужен только как формальная реализация интерфейса.

[EventClass] [Guid(EventsGuids.eventClassId)] [Transaction(TransactionOption.Disabled)] public class LceEvents: ServicedComponent, ILceMessage { public void TriggerEvent(string message) { } }

Издатель события может быть как обслуживаемой компонентой, так и не быть

связанным с контекстом COM+. Следует учитывать, что при отсутствии у события

подписчиков будет выброшено исключение COM+.

public class LcePublisher: ServicedComponent { public LcePublisher() { } public void DoWork() { ILceMessage lcEvent = (ILceMessage) new LceEvents(); try { while (true) { lcEvent.TriggerEvent("Событие!"); Thread.Sleep(1000); } } catch (System.Runtime.InteropServices.COMException) { Console.WriteLine("Нет подписчиков"); } } }

Page 124: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

119

class MainApp { public static void Main() { LcePublisher com = new LcePublisher(); com.DoWork(); } } // LcePublisher.cs

Подписчик события является компонентой COM+, реализующей интерфейс события.

Метод данного интерфейса вызывается при публикации события. В качестве примера

при обработке события подписчик проверяет наличие частной очереди сообщений и

посылает в нее сообщение, после чего сообщает об успехе транзакции. Для простоты

примера имя очереди указано как константа, реальные приложения должны хранить его

в файле конфигурации.

// LceSubscriber.cs using System; using System.IO; using System.Threading; using System.Runtime.InteropServices; using System.EnterpriseServices; using System.Messaging; using Seva.ComUtils; using Seva.Msmq; [assembly: ApplicationName("LCE demo")] [assembly: ApplicationActivation(ActivationOption.Server)] [assembly: ApplicationAccessControl(false)] public static class Consts { public const string testQueue = @".\Private$\sample_queue"; } [JustInTimeActivation] [Transaction(TransactionOption.Required)] public class LceSubscriber : ServicedComponent, ILceMessage { public LceSubscriber() { }

В качестве примера при обработке события подписчик проверяет наличие частной

очереди сообщений и посылает в нее сообщение, после чего информирует координатор

транзакций об успехе транзакции.

Page 125: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

120

public void TriggerEvent(string message) { MessageQueue queue = MsmqTools.CreateQueue(Consts.testQueue); queue.Send(message, MessageQueueTransactionType.Automatic); ContextUtil.SetComplete(); } } class MainApp { public static void Main() { // Для ленивой регистрации приложения создается // служебная компонента LceSubscriber subscriber = new LceSubscriber(); // Создание постоянной подписки LceUtils.PermanentSubscription(Consts.comAppName, typeof(LceSubscriber).ToString(), EventsGuids.eventClassId, EventsGuids.interfaceId); } } // LceSubscriber.cs

Make-файл для создания подписчика и издателя.

all: LceSubscriber.exe LcePublisher.exe LceSubscriber.snk: sn -k LceSubscriber.snk LcePublisher.snk: sn -k LcePublisher.snk LceSubscriber.exe: Seva*.cs LceEvents.cs LceSubscriber.cs LceSubscriber.snk csc /out:LceSubscriber.exe Seva*.cs LceEvents.cs LceSubscriber.cs /r:interop.comadmin.dll /keyfile:LceSubscriber.snk LcePublisher.exe: Seva*.cs LceEvents.cs LcePublisher.cs LcePublisher.snk csc /out:LcePublisher.exe Seva*.cs LceEvents.cs LcePublisher.cs /r:interop.comadmin.dll /keyfile:LcePublisher.snk

Сервисы COM+ без компонент COM+

Сервисы без компонент (SWC) позволяют использовать сервисы среды COM+ без

создания приложение COM+ и без наследования от класса ServicedComponent.

Для включения возможностей SWC следует включить сначала доступ по TCP/IP к

координатору распределенных транзакций. По умолчанию эта возможность отключена

в Windows 2003 и Windows XP SP2, поскольку уменьшает безопасность системы. Если

компьютер подключен к интернету через NAT, то ее включение достаточно безопасно.

Эту опцию можно включить через оснастку %systemroot%\system32\Com\comexp.msc

или импортом следующего файла в системный реестр:

Page 126: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

121

REGEDIT4 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSDTC\Security] "NetworkDtcAccess"=dword:00000001

Затем следует перезапустить службу координатора распределенных транзакций

net stop MSDTC net start MSDTC

Рассмотрим класс, облегчающий использование SWC. Поскольку использование COM+

связано с использованием неуправляемых ресурсов, класс реализует интерфейс

System.IDisposable с единственным методом void Dispose(), осуществляющим выход их

домена COM+.

using System; using System.EnterpriseServices; namespace Seva.Swc { public class SwcUtil : IDisposable { public SwcUtil() { ServiceDomain.Enter(GetConfig(TransactionOption.Required)); } public SwcUtil(TransactionOption transactionOption) { ServiceDomain.Enter(GetConfig(transactionOption)); } protected virtual ServiceConfig GetConfig( TransactionOption transactionOption) { ServiceConfig config = new ServiceConfig(); config.Transaction = transactionOption; config.TrackingEnabled = true; config.TrackingAppName = "SwcUtil"; config.TrackingComponentName = this.GetType().FullName; return config; } public void Dispose() { ServiceDomain.Leave(); } } }

Пример использования данного класса приведен ниже. В качестве примера рассмотрена

работа с очередями сообщений с использованием транзакций COM+.

Page 127: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

122

using System; using System.EnterpriseServices; using System.Messaging; using Seva.Msmq; using Seva.Swc; public class MainApp { public static void Main() { using (SwcUtil swc = new SwcUtil()) { MessageQueue queue = MsmqTools.CreateQueue(@".\Private$\swc_queue"); queue.Send("SWC Message", MessageQueueTransactionType.Automatic); Console.WriteLine("Нажмите <Enter> для завершения транзакции"); Console.ReadLine(); ContextUtil.SetComplete(); } } }

Использование SWC не требует подписанной сборки, поскольку сборка не

регистрируется в качестве приложения COM+.

К сожалению, при использовании SWC невозможно использовать компенсирующий

менеджер ресурсов. Во-первых, класс Compensator наследован от

System.EnterpriseServices.ServicedComponent, то есть является обслуживаемой

компонентой, поэтому для его использования должно быть создано приложение COM+.

Поэтому при создании секретаря класса Clerk и регистрации компенсатора при

использовании сервисов без компонент происходит catastrophic exception, если сборка

подписана, иначе – жалоба на отсутствие подписи. Таким образом, сервисы без

компонент следует считать полумерой, благодаря которой можно работать с ресурсами,

поддерживающими распределенные транзакции, такими как MSMQ или MS SQL.

6.4. Выводы по использованию среды Enterprise Services / COM+

Хотя COM+ имеет обширные возможности для создания распределенных систем

внутри предприятия, ей свойственны и следующие недостатки.

1. Среда COM+ разработана до .NET Framework, поэтому в среде Enterprise Services

существуют ограничения на классы .NET Framework, регистрируемые в качестве

обслуживаемых компонент. При использовании компонент Enterprise Services

Page 128: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

123

проявляются некоторые особенности работы с ними, являющиеся следствием

нетривиального взаимодействия CLR и COM+.

2. Компоненты COM+ не могут использоваться вне доверенной сети, поскольку для

их использования должен быть открыт, в частности, доступ к порту RPC (135-ый порт

TCP). С данным портом связан большой и, вероятно, еще незаконченный список

общеизвестных уязвимостей.

Рассмотрим промежуточную среду COM+ с точки зрения требований к распределенной

системе.

1. Открытость. Служба COM+ является внутренней технологией Microsoft и

реализована только в операционной системе Windows 2000 и последующих версиях

Windows. Для использования обслуживаемой компоненты нужно иметь как минимум

доступ к сборке с ее интерфейсом и иметь установленный посредник компоненты

COM+. Таким образом, среда Enterprise Services не является открытой.

2. Масштабируемость. Служба COM+ поддерживает балансировку нагрузок путем

создания кластера машин на основе Windows Server. Выбор используемого сервера

осуществляется только в момент создания объекта клиентом, даже при использовании

JIT-активации. Среда COM+ поддерживает модель единственного вызова с пулом

объектов, что позволяет добиться баланса времени создания удаленного объекта и

используемой сервером памяти.

3. Поддержание логической целостности данных. COM+ использует координатор

распределенных транзакций из сервера транзакций Microsoft (MTS) и позволяет

создавать менеджеры управления ресурсами.

4. Устойчивость. Посредники приложения COM+ связывают клиентский компьютер с

именем компьютера (DNS или NETBIOS), на котором развернуто приложение COM+.

Таким образом, этот компьютер является слабым местом системы (в случае

использования кластера приложений им является планировщик кластера COM+). Для

решения этой проблемы можно использовать сервисы Windows Clustering,

позволяющие создать дублера данного компьютера, начинающего функционировать в

Page 129: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 6. Сервисы распределенных систем COM+

124

случае выхода основного планировщика. Таким образом, использующая среду COM+

распределенная система может не иметь уникальной точки сбоя.

5. Эффективность (в узком смысле). Среда COM+ взаимодействует со средой .Net

достаточно сложным образом, вероятно приводящим к определенным накладным

расходам.

6. Безопасность. COM+ позволяет использовать встроенные механизмы безопасности

Microsoft Windows.

Можно сделать вывод, что хотя промежуточная среда EnterpriseServices/COM+

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

ее использование ограничено взаимодействием компонент внутри локальной или

виртуальной частной сети, построенной на базе Microsoft Windows в пределах одного

предприятия.

Page 130: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 7. Промежуточная среда веб-служб ASP.NET

125

Тема 7. Промежуточная среда веб-служб ASP.NET

7.1. Введение в веб-службы

Веб-службой или веб-сервисом (web service, WS) называется программная компонента,

предоставляющая сервис удаленного вызова на основе группы стандартов WS-I (Web

Services Interoperability), основными из которых являются протокол обмена

сообщениями SOAP, язык описания интерфейса WSDL, HTTP как основной

транспортный протокол, а также XML и схемы XML. Для описания спецификаций

формата сообщений в веб-службах в настоящее время обычно используется схема XML

и кодирование тела пакета SOAP-Document.

Служба ASP.NET организует на основе данных стандартов сервис удаленного вызова

методов объектов. ASP.NET создает и публикует WSDL-описание веб-службы на

основе интерфейса класса .NET, обрабатывает приходящие от клиентов сообщения

SOAP, вызывает методы объекта с извлеченными из сообщений SOAP параметрами, а

затем передает клиенту сообщение с результатом выполнения метода (рис. 7.1). Таким

образом, полное название промежуточной среды – веб-службы ASP.NET.

Сервер

ASP.NET

Клиент

Клиент

Интернет

SOAP-ответ

SOAP-запрос

Вызоввеб-метода

Посредник

Диспетчер

Объект

Вызов веб-метода

ФильтрыпакетовSOAP

ФильтрыпакетовSOAP

Рисунок 7.1. Архитектура веб-службы ASP.NET

Хотя с точки зрения прикладного разработчика использование веб-службы

представляется вариантом удаленного вызова через интернет, в основе веб-служб

Page 131: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 7. Промежуточная среда веб-служб ASP.NET

126

лежит расширяемая технология обмена сообщениями SOAP, независимая от

транспортного уровня. В частности, существуют стандарты, описывающие

маршрутизацию сообщений SOAP, что придает веб-службам признаки среды обмена

сообщениями. Благодаря гибкой технологии возможно расширение промежуточной

среды веб-служб как по мере принятия новых стандартов, так и при необходимости в

нестандартной обработке пакетов SOAP. Это возможно за счет фильтров SOAP,

которые образуют трубопровод (pipeline), обрабатывающий пакеты SOAP. На

платформе .NET Framework 2.0 за преобразование пришедшего по HTTP сообщения

SOAP в вызов метода веб-службы отвечает служба ASP.NET, а за организацию

расширяемого набора фильтров SOAP – расширение, называемое WSE (Web Service

Enhancements). В качестве приложения-носителя для ASP.NET, получающего

сообщение по транспортному протоколу, обычно используется служба Microsoft

Internet Information Services, IIS (рис. 7.2) .

Сокеты

IIS / http.sys

ASP.NET +WSE

WSE 3.0

Веб-служба

Вызовметода

ПакетыTCP/IP

Веб служба сиспользованием

HTTP, IIS, ASP.NET

Веб служба сиспользованием

TCP, WSE

Веб-служба

Вызовметода

ПакетыSOAP

ПакетыHTTP

ПакетыTCP/IP

ПакетыSOAP

Рисунок 7.2. Возможные схемы реализации веб-службы

Page 132: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 7. Промежуточная среда веб-служб ASP.NET

127

При использовании WSE 3.0 возможно создание веб-служб поверх протокола TCP без

применения IIS. В приложении II так же приведен пример использования ASP.NET без

использования IIS.

Клиентом веб-службы ASP.NET может быть компонента, созданная на .NET

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

стандартов веб-служб. При использовании .NET Framework на основе WSDL-описания

сервиса специальные утилиты создают класс посредника веб-службы, который

используется клиентом сервиса. Таким же образом создаются на .NET Framework и

клиенты веб-сервисов, отличных от веб-служб ASP.NET. Кроме того, в наиболее

известной альтернативной реализации CLI – проекте Mono – в настоящий момент

присутствует поддержка страниц и веб-служб ASP.NET (версии 1.1 и частично 2.0) для

серверов Apache и XSP.

Веб-службы нашли широкое применение как средство построения распределенных

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

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

для промышленного применения. В частности, это касается стандартов WS-Transaction

и WS-BusinessActivity, которые дают возможность использовать распределенные

транзакции, включающее в себя несколько веб-служб. В настоящий момент эти

стандарты только утверждаются, и будут реализованы, в частности, в .NET Framework

3.0. В настоящее же время веб-службы ASP.NET могут являться только корневым

объектом транзакции, но не могут участвовать в транзакциях, начатых их клиентом.

Широкое практическое применение веб-служб привело и к тому, что некоторые

связанные с ними стандарты уже устарели или не нашли столь широкого применения,

как ожидалось. Прежде всего это касается стандарта UDDI (Universal Description,

Discovery, and Integration), который является частью WS-I и предназначается для

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

Для построения сервера с каталогом веб-служб можно использовать Microsoft Windows

2003 или систему на основе J2EE. Несколько крупных и известных компаний

поддерживали публичные каталоги веб-сервисов, однако эта инициатива была закрыта

в конце 2005-го года. Хотя идея поиска веб-служб считалась одной из

Page 133: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 7. Промежуточная среда веб-служб ASP.NET

128

основополагающих, в настоящий момент нет публичных каталогов веб-служб, а для

развертывания даже в пределах крупных предприятий достаточно сложная

спецификация UDDI представляется избыточной. Можно заключить, что на практике

идея каталогов веб-служб не нашла ожидаемого применения.

7.2. Использование расширения WSE

Для расширения функциональности веб-служб ASP.NET служит надстройка WSE. WSE

3.0 позволяет так же создавать безопасные службы без использования IIS, используя

любое приложение в качестве носителя, но при этом выбор транспортного протокола

ограничен протоколом TCP. Установка WSE приводит к созданию гибкой

настраиваемой промежуточной среду для использования веб-сервисов, что позволяет

как использовать дополнительные стандарты, так и реализовывать нестандартные

расширения благодаря SOAP-фильтрам, обрабатывающих передаваемые между

клиентом и сервером пакеты SOAP. Последовательность фильтров образует

трубопровод (pipeline), рис. 7.3.

Сервер WSEКлиент WSE

Трубопровод запроса

Трубопроводответа

Расш

ирение

Расш

ирение

Расш

ирение

Расш

ирение

Посредник

служ

бы

Веб

-служба

Фильтр Фильтр

Фильтр Фильтр

Фильтр Фильтр

Фильтр Фильтр

Политика посредника Политика службы

Рисунок 7.3 Архитектура WSE

Page 134: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 7. Промежуточная среда веб-служб ASP.NET

129

Основное достоинство WSE заключается в отделении политики доступа (policy) к

веб-службе от самой службы. Политика обычно является набором требований и

ограничений для клиента веб-службы. Политика включает в себя те или иные

расширения (assertions или extensions), а также ограничивает доступ

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

Каждое из расширений WSE может создавать один или два фильтра (для исходящих и

входящих пакетов) как на клиенте, так и на сервере (при наличии сборки с

расширением на обеих сторонах обмена).

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

конфигурационные файлы (рис 7.4). Как на клиенте, так и на сервере политика

хранится в отдельном файле (по умолчанию wse3policyCache.config), а в файле

конфигурации клиента или службы (web.config) указывается ссылка на нее. Данные

файлы можно редактировать при помощи утилиты WseConfigEditor3.exe или вручную.

Сервер WSEКлиент WSE

Файл конфигурацииapp.exe.config

Файл политикwse3policyCache.config

Сборки срасширениями

Файл конфигурацииweb.config

Файл политикwse3policyCache.config

Сборки срасширениями

Сборки сменеджерами

Рисунок 7.4 Конфигурация WSE

Далее приведен пример файла конфигурации web.config при использовании WSE.

Раздел <system.web><webServices><soapServerProtocolFactory> включает WSE в

цепочку обработки сообщений SOAP. Раздел <microsoft.web.services3><diagnostics>

позволят вести журнал всех принятых и отправленных пакетов, что может быть

полезно на этапе тестирования.

Page 135: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 7. Промежуточная среда веб-служб ASP.NET

130

<?xml version="1.0" encoding="utf-8"?> <configuration> <system.web> <webServices> <soapServerProtocolFactory type="Microsoft.Web.Services3.WseProtocolFactory, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> </webServices> <compilation debug="false"> <assemblies> <add assembly="Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> </assemblies> </compilation> </system.web> <microsoft.web.services3> <diagnostics> <trace enabled="true" input="C:\Inetpub\logs\InputTrace.webinfo" output="C:\Inetpub\logs\OutputTrace.webinfo" /> </diagnostics> <policy fileName="wse3policyCache.config" /> </microsoft.web.services3> </configuration>

Файл политики будет подробнее рассмотрен далее в примере создания собственных

расширений и фильтров.

7.3. Создание веб-службы в среде .NET Framework

Будучи основаными на обмене сообщениями, веб-службы поддерживают только одну

модель удаленного вызова – активацию объекта сервером на время единственного

удаленного вызова. Реализация веб-сервисов в ASP.NET не имеет поддержки пула

объектов. Доступ к каким либо свойствам или полям удаленного объекта невозможен,

поскольку сообщения SOAP в ASP.NET связываются только с методами объекта.

Описание веб-службы

При использовании ASP.NET в директории IIS (по умолчанию это C:\Inetpub\wwwroot\)

должен быть создан файл с расширением .asmx и строкой следующего вида.

<%@ WebService class="SampleService" language="C#" %>

Она означает, что при доступе к связанному с данным файлом URL по протоколам

SOAP/HTTP будет активирована веб-служба на основе класса SampleService. Класс

веб-службы может быть либо описан в этом же файле, либо помещен в сборке в

подкаталоге bin. Класс веб-службы не может быть классом общего вида. Класс

Page 136: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 7. Промежуточная среда веб-служб ASP.NET

131

веб-службы обычно имеет необязательный атрибут

System.Web.Services.WebServiceAttribute, имеющих два основных свойства –

Description с текстовым описанием службы и Namespace с указанием уникального

пространства имен, используемого при генерации описания интерфейса веб-службы на

языке WSDL. В его роли должен выступать уникальный URI, обычно связанный с

адресом домена веб-службы. Пространство имен не обязательно должно являться

некоторым URL. Класс веб-службы может быть унаследован от класса

System.Web.Services.WebService. Необходимым на практике атрибутом класса

веб-службы при использовании WSE 3.0 является также атрибут

Microsoft.Web.Services3.PolictyAttribute, который задает имя политики, в дальнейшем

связываемое через файлы конфигурации с расширениями или списком допущенных

пользователей.

Для того, чтобы включить метод класса веб-службы в его общедоступный интерфейс, к

методу применяется атрибут System.Web.Services.WebMethodAttribute. Он имеет два

важных поля – Description и TransactionOption. Последний позволяет стать веб-методу

корневым объектом новой транзакции. Происходит это аналогично использованию

сервисов без компонент COM+. Следует отметить, что веб-метод может быть либо

только корнем транзакции, либо не участвовать в ней. Поддержка распределенных

между несколькими веб-службами транзакций в .NET Framework 2.0 / WSE 3.0

отсутствует. Следующий веб-метод является корнем новой транзакции.

<%@ WebService class="BankAccount" language="C#" %> using System; using System.Web.Services; using System.EnterpriseServices; using Microsoft.Web.Services3; [WebService(Namecpace=”http://super.bank/webservices”)] [Policy(”BankAccountService”)] public class BankAccount : WebService { [WebMethod(TransactionOption=TransactionOption.RequiresNew] public void Transfer(double Amount) { // ... // необязательное завершение транзакции в явном виде ContextUtil.SetComplete(); } }

Page 137: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 7. Промежуточная среда веб-служб ASP.NET

132

Еще двумя важными атрибутами веб-методов являются SoapRpcMethodAttribute и

SoapDocumentMethodAttribute. Они определяют описание интерфейса метода на языке

WSDL. В частности, при атрибуте SoapDocumentMethodAttribute будет использоваться

кодирование параметров SOAP-Document, что в настоящее время является стандартом,

а при использовании SoapRpcMethodAttribute будет применяться кодирование

SOAP-RPC, в настоящее время признанное устаревшим. Следует отметить, что для

преобразования содержимого тела пакета SOAP в параметры метода в обоих случаях

используется класс XmlSerializer, поэтому на типы аргументов и результатов

веб-метода действуют все описанные в главе о сериализации ограничения, связанные с

классом XmlSerializer. Атрибуты SoapDocumentMethod и SoapRpcMethod также

содержат свойство OneWay, позволяющее клиенту производить односторонний вызов

метода службы.

Создание посредника веб-службы

При использовании WSE 3.0 для создания посредника следует использовать утилиту

WseWsdl3.exe. Она запускается следующим образом.

WseWsdl3.exe http://localhost/service.asmx?wsdl /type:webClient /out:proxy.cs

Утилита использует указанный URL WSDL-описания службы и создаст на его основе

файл proxy.cs с исходным текстом класса-посредника, наследованного от

Microsoft.Web.Services3.WebServicesClientProtocol. Описание веб-сервиса на языке

WSDL создается автоматически службой ASP.NET или иной системой веб-служб.

Поскольку файл c кодом посредника может быть создан заново в дальнейшем, в него не

рекомендуется вносить какие-либо изменения. Например, для связывания посредника с

политикой WSE следует воспользоваться частичным описанием класса следующим

образом.

[Policy("BankClientPolicy")] public partial class BankService : Microsoft.Web.Services3.WebServicesClientProtocol { }

Следует отметить, что, как и при применении утилиты xsd.exe для генерации кода по

XML-схеме, созданный посредник реализует не совсем тот же интерфейс, что

Page 138: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 7. Промежуточная среда веб-служб ASP.NET

133

исходный класс. Связано это с преобразованием типов данных CLR к типам данным

XSD-схем при создании описания интерфейса, и обратным преобразованием при

создании посредника (рис. 7.5). В частности, как типизированные, так и

нетипизированные списки класса службы в ее посреднике превращаются в массивы.

Классвеб-службы ASP.NET WSDL-документ WseWsdl3.exe

wsdl.exeКласс

посредника

Рисунок 7.5. Создание посредника веб-службы

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

Например, если служба имеет метод Transfer, то будут созданы следующие методы:

– Transfer – для обычного вызова веб-метода;

– BeginTransfer и EndTransfer – для асинхронного вызова этого же метода через

BeginInvoke/EndInvoke;

– TransferAsync (два метода) – для асинхронного вызова метода через

InvokeAsync.

Разработчик может и самостоятельно использовать BeginInvoke/EndInvoke/InvokeAsync

с аналогичным результатом, речь идет просто об экономии времени разработчика. В

посреднике так же создается метод CancelAsync (один на каждый веб-класс) для

отмены вызовов удаленных методов через InvokeAsync.

Утилита WseWsdl3.exe поддерживает только работу с методами, имеющими

единственный параметр или же не имеющими ни одного. Аналогичная по назначению

стандартная утилита wsdl.exe из .NET SDK, не использующая WSE, не имеет такого

ограничения. Данное ограничение следует считать оправданным и даже желательным

для вызываемых удаленно методов, как объяснялось во второй главе данного курса.

7.4. Реализация нестандартного расширения WSE

В качестве примера использования расширяемой архитектуры веб-служб на основе

ASP.NET представлено расширение WSE, которое ограничивает доступ к веб-службе

Page 139: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 7. Промежуточная среда веб-служб ASP.NET

134

некоторым интервалом времени (например только с 10 часов вечера до 8 утра).

Данному расширению достаточно создать единственный фильтр SOAP – входящий

фильтр сервера. Далее приведен исходный текст сборки с расширением,

унаследованным от абстрактного класса Microsoft.Web.Services3.Design.PolicyAssertion.

// TimeAssertion.cs using System; using System.IO; using System.Reflection; using System.Xml; using System.Xml.Serialization; using System.Collections.Generic; using Microsoft.Web.Services3; using Microsoft.Web.Services3.Design; using Microsoft.Web.Services3.Security; [assembly:AssemblyVersionAttribute("1.0.0.0")] namespace Seva.WS.Assertions { public class TimeAssertion: PolicyAssertion { private TimeIntervals intervals; public TimeAssertion(): base() { intervals = null; }

В методе ReadXml расширение должно считать свои параметры из файла политики.

Единственным параметром расширения будет атрибут с именем файла с разрешенным

интервалом времени.

public override void ReadXml(XmlReader reader, IDictionary<string, Type> extensions) { string fileName = reader.GetAttribute("file"); if (fileName == null) throw new Exception("Attribute 'file' not found in policy"); // Обязательная операция - переход к следующему тегу в файле политики, // иначе данный элемент будет читаться бесконечно reader.Read(); // Чтение файла с интервалами времени доступности службы using (StreamReader timeReader = new StreamReader(fileName)) { XmlSerializer serializer = new XmlSerializer(typeof(TimeIntervals)); intervals = (TimeIntervals) serializer.Deserialize( new XmlTextReader(timeReader)); } }

Page 140: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 7. Промежуточная среда веб-служб ASP.NET

135

Методы Create....Filter отвечают за создание экземпляров фильтров SOAP, которые

будут встроены в трубопровод фильтров. В данном примере только метод

CreateServiceInputFilter возвращает созданный фильтр, но все остальные методы также

обязаны присутствовать.

public override SoapFilter CreateClientInputFilter( FilterCreationContext context) { return null; } public override SoapFilter CreateClientOutputFilter( FilterCreationContext context) { return null; } public override SoapFilter CreateServiceInputFilter( FilterCreationContext context) { return new CheckTimeFilter(intervals, context); } public override SoapFilter CreateServiceOutputFilter( FilterCreationContext context) { return null; } }

Класс фильтра унаследован от Microsoft.Web.Services3.SoapFilter и хранит в себе

список разрешенных интервалов. Это не лучшее решение, но оно позволяет сократить

пример и не привязывать его к использованию баз данных.

public class CheckTimeFilter : SoapFilter { private TimeIntervals intervals; public CheckTimeFilter(TimeIntervals intervals, FilterCreationContext context) { this.intervals = intervals; }

ProcessMessage – основной метод класса, обрабатывающий пакет SOAP. В данном

примере в нем нет нужды анализировать содержимое пакета SoapEnvelope.

Page 141: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 7. Промежуточная среда веб-служб ASP.NET

136

public override SoapFilterResult ProcessMessage(SoapEnvelope envelope) { if (! intervals.Check(DateTime.Now)) throw new Exception("Service is unavailable now."); return SoapFilterResult.Continue; } }

Класс TimeInterval содержит в себе интервал времени в течении суток. Тип TimeSpan, в

отличие от DateTime, не относится к примитивным и не может быть напрямую

сохранен в атрибуте XML, поэтому приходится использовать некоторый обходной путь

для автоматической сериализации объекта этого класса в строку удобного вида, такую

как следующая.

<TimeInterval Inverse="false" From="09:00:00" To="10:30:00" />

Для сериализации в указанный вид можно либо реализовать чтение из XML

самостоятельно, либо использовать дополнительные свойства сдля XML-сериализации.

[Serializable] public class TimeInterval { [XmlAttribute("From")] public string FromField { set {From = TimeSpan.Parse(value);} get {return From.ToString();} } [XmlAttribute("To")] public string ToField { set {To = TimeSpan.Parse(value);} get {return To.ToString();} } [XmlAttribute("Inverse")] public bool Inverse; [XmlIgnore] public TimeSpan From; [XmlIgnore] public TimeSpan To; public bool Check(DateTime Time) { bool result = (From <= Time.TimeOfDay) && (Time.TimeOfDay <= To); if (Inverse) result = ! result; return result; } }

Page 142: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 7. Промежуточная среда веб-служб ASP.NET

137

Класс TimeIntervals содержит метод проверки принадлежности времени заданному

интервалу.

[XmlRoot("TimeIntervals")] public class TimeIntervals: List<TimeInterval> { public TimeIntervals() { } public bool Check(DateTime Time) { foreach (TimeInterval interval in this) if (interval.Check(Time)) return true; return false; } } }

Файлы сборки с расширениями WSE удобно поместить в глобальный кеш сборок GAC.

Нижеследующий make-файл создает и регистрирует сборку в GAC при команде

nmake && nmake install.

# Файл: makefile all: Seva.WS.TimeAssertion.dll Seva.WS.TimeAssertion.dll: TimeAssertion.cs TimeAssertion.key csc /t:library /out:Seva.WS.TimeAssertion.dll /keyfile:TimeAssertion.key /r:Microsoft.Web.Services3.dll TimeAssertion.cs TimeAssertion.key: sn -k TimeAssertion.key install: gacutil -nologo -i Seva.WS.TimeAssertion.dll`

Для использования данной политики нужно выполнить следующие действия.

1. Создать файл с допустимым местным временем использования веб-службы,

например такой.

<?xml version="1.0" encoding="utf-8"?> <TimeIntervals> <TimeInterval Inverse="true" From="09:00:00" To="20:00:00" /> <TimeInterval Inverse="false" From="13:00:00" To="13:30:00" /> </TimeIntervals>

2. Создать файл wse3policyCache.config на сервере со следующим содержанием (для

простоты предполагается отсутствие иных политик, также следует указать реальное

значение публичного ключа сборки с созданным расширением WSE).

Page 143: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 7. Промежуточная среда веб-служб ASP.NET

138

<policies xmlns="http://schemas.microsoft.com/wse/2005/06/policy"> <extensions> <extension name="timeIntervals" type="Seva.WS.Assertions.TimeAssertion, Seva.WS.TimeAssertion, Version=1.0.0.0, Culture=neutral, PublicKeyToken=..." /> </extensions> <policy name="MyServicePolicy"> <timeIntervals file="C:\Inetpub\wwwroot\timeschema.config" /> </policy> </policy> </policies>

Вместо MyServicePolicy должно быть указано название политики, указанной в атрибуте

Policy класса веб-службы.

7.5. Менеджер пользовательских записей

В WSE уже входят расширения, позволяющие использовать в политике доступа к

веб-службе несколько видов аутентификации пользователей, а именно:

– аутентификация Kerberos (в пределах домена Active Directory);

– аутентификация на основе сертификатов X.509;

– аутентификация на основе имени пользователя и пароля, используемая вместе с

серверным сертификатом (для шифрования трафика) или без него (в этом случае

рекомендуется шифрование передаваемой информации на уровне транспортного

протокола TCP).

Тот или иной способ аутентификации можно установить в политике при помощи

утилиты WseConfigEditor3.exe или прямым редактированием файла политики WSE.

По умолчанию WSE использует для проверки имени и пароля список пользователей

Windows, что не всегда может быть удобно. Для ведения нестандартного списка

пользователей веб-службы можно установить свой менеджер пользовательских

записей. Для реализации нестандартной проверки подлинности в WSE имеется

механизм так называемых поставщиков токенов безопасности

(Microsoft.Web.Services3.Design.TokenProvider). Такие поставщики не являются сами по

себе расширениями и не имеют доступа к пакету SOAP, а используется другими

расширениями WSE. К последним относятся, в частности, стандартные расширения

UsernameOverTransportAssertion и UsernameForCertificateAssertion, использующие

менеджер пользовательских записей в момент проверки подлинности имени и пароля

пользователя.

Page 144: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 7. Промежуточная среда веб-служб ASP.NET

139

Менеджеры описываются в файле web.config в разделе

<microsoft.web.services3><security><securityTokenManager>. Каждый менеджер

связывается с каким либо именем токена, указанным в атрибуте localName, например

как указано ниже.

<microsoft.web.services3> <security> <securityTokenManager> <add localName="UsernameToken" type="Seva.WS.Users.UsersListManager, Seva.WS.UsersManager, Version=1.0.0.0, Culture=neutral, PublicKeyToken=..." namespace="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" > <users file="C:\Inetpub\users.config"/> </add> </securityTokenManager> </security> <policy fileName="wse3policyCache.config" /> </microsoft.web.services3>

При обнаружении использующим аутентификацию расширением записи о токене в

заголовке пакета SOAP (в разделе <wsse:Security>) оно использует связанный с данным

токеном менеджер, вызывая его метод VerifyToken. По умолчанию установлен

менеджер UsernameTokenManager, связанный с записью в заголовке SOAP вида

<wsse:UsernameToken>. Нестандартный менеджер токенов может быть либо

унаследован от класса UsernameTokenManager, изменив его функциональность, либо

реализован с чистого листа наследованием класса SecurityTokenManager.

К сожалению, при создании наследника класса UsernameTokenManager следует

учитывать, что метод AuthenticateToken создаваемого менеджера должен возвращать

тот же пароль, который передается в пакете SOAP. Однако хранение самих паролей в

базе пользователей – решение, с точки зрения безопасности, неверное. Вместо самого

пароля в базе следует хранить его образ (хеш), по которому невозможно восстановить

сам пароль. Однако такая реализация метода AuthenticateToken имеет свои основания.

Проблема в том, что пароль в заголовках пакете SOAP может передаваться как в

открытом виде (при этом требуется шифрование пакета на основе, например,

сертификатов X.509) , так и в виде своего образа (digest). Образ вычисляется как хеш от

конкатенации некоторой случайной строки, указанной в пакете (nonce), времени

создания пакета и самого пароля:

Password_Digest = Base64(SHA1( nonce + created + password)),

Page 145: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 7. Промежуточная среда веб-служб ASP.NET

140

где SHA1 – широко применяемая криптографическая функция вычисления хеша. Она

обеспечивает разный результата для разных аргументов с высокой вероятностью и

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

для проверки пароля пользователя в данном случае необходимо иметь доступ к самому

паролю, а не к его образу. Наилучшим решением данной проблемы будет отказ от

передачи образа пароля в пакете и использования защиты всего сообщения на основе

сертификатов или защита канала передачи данных на основе SSL.

Предлагаемый далее менеджер паролей работает в обеих случаях – при использовании

открытых паролей в сообщении он предполагает, что в базе пользователей хранятся

хеши паролей (метод VerifyPlainTextPassword). Этот вариант будет задействован, в

частности, при использовании стандартных расширений. При использовании же

хешированных паролей в открытом сообщении предполагается, что в базе

пользовательских записей хранятся сами пароли. Этот вариант будет задействован при

добавлении клиентом в пакет SOAP элемента <wsse:UsernameToken>, создаваемого

классом UsernameToken с параметром PasswordOption.SendHashed. Вариант такого

расширения для клиента веб-службы будет описан далее.

// UsersManager.cs using System; using System.IO; using System.Reflection; using System.Text; using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; using System.Collections.Generic; using System.Security.Cryptography; using System.Security; using Microsoft.Web.Services3; using Microsoft.Web.Services3.Design; using Microsoft.Web.Services3.Security; using Microsoft.Web.Services3.Security.Tokens;

Поскольку для использования в политике WSE сборку удобнее зарегистрировать в

GAC, то следует указать номер версии сборки.

Page 146: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 7. Промежуточная среда веб-служб ASP.NET

141

[assembly:AssemblyVersionAttribute("1.0.0.0")] namespace Seva.WS.Users { public class UsersListManager: UsernameTokenManager { private UsersList users; public UsersListManager() { users = new UsersList(); }

Основной конструктор класса UsersListManager должен загрузить список

пользователей из указанного в конфигурации файла. На практике в случае большого

числа пользователей следует использовать СУБД и запрос к базе данных

пользователей непосредственно в методе AuthenticateToken.

public UsersListManager(XmlNodeList configData): base(configData) { string fileName = configData[0].Attributes["file"].Value; users = UsersList.Load(fileName); } protected override string AuthenticateToken(UsernameToken token) { if (!users.Users.ContainsKey(token.Username)) return null; return users.Users[token.Username]; }

Метод VerifyPlainTextPassword модифицирован для работы с образами паролей.

protected override void VerifyPlainTextPassword(UsernameToken token, string authenticatedPassword) { if (token==null) throw new ArgumentNullException("token is null"); String hashed = Utils.HashedPassword(token.Password); if (authenticatedPassword==null || authenticatedPassword=="" || hashed!=authenticatedPassword) throw new Exception("Passwords does not match "); } }

Список пользователей хранится в объекте UsersList. Поскольку обычные классы

словарей не могут быть использованы классом форматирования XmlSerialiser, то

список пользователей реализует интерфейс IXmlSerializable.

Page 147: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 7. Промежуточная среда веб-служб ASP.NET

142

[XmlRoot("users")] public class UsersList: IXmlSerializable { private Dictionary<string, string> usersField; public Dictionary<string, string> Users { get {return usersField;} } public UsersList() { usersField = new Dictionary<string, string>(); } public void ReadXml(XmlReader reader) { reader.Read(); while (reader.NodeType != XmlNodeType.EndElement) { Users.Add(reader.GetAttribute("username"), reader.GetAttribute("password")); reader.Read(); } } public void WriteXml(XmlWriter writer) { foreach (string user in Users.Keys) { writer.WriteStartElement("user"); writer.WriteAttributeString("username", user); writer.WriteAttributeString("password", this.Users[user]); writer.WriteEndElement(); } } public XmlSchema GetSchema() { return null; }

Метод NewUser используется для добавления нового пользователя.

public void NewUser(string userName, string password, bool hashed) { if (Users.ContainsKey(userName)) { throw new ApplicationException("Duplicate username."); } if (hashed) { password = Utils.HashedPassword(password); } Users.Add(userName, password); }

Page 148: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 7. Промежуточная среда веб-служб ASP.NET

143

Методы Load и Save записывают список пользователей и их паролей (или их образов) в

файл.

public static UsersList Load(string fileName) { XmlSerializer serializer = new XmlSerializer(typeof(UsersList)); using (StreamReader reader = new StreamReader(fileName)) { return (UsersList) serializer.Deserialize(reader); }; } public void Save(string fileName) { XmlSerializer serializer = new XmlSerializer(typeof(UsersList)); using (StreamWriter writer = new StreamWriter(fileName)) { serializer.Serialize(writer, this); }; } }

Статический класс Utils облегчает вызов функции хеширования и преобразует ее

результат в строку.

static class Utils { public static string HashedPassword(string password) { SHA1CryptoServiceProvider crypto = new SHA1CryptoServiceProvider(); byte[] inputBuffer = Encoding.Unicode.GetBytes(password); return Convert.ToBase64String(crypto.ComputeHash(inputBuffer)); } }

Следующий make-файл создает сборку расширения c менеджером пользователей и

регистрирует ее в GAC после команды nmake && nmake install.

all: Seva.WS.UsersManager.dll Seva.WS.UsersManager.dll: UsersManager.cs UsersManager.key csc /t:library /out:Seva.WS.UsersManager.dll /keyfile:TimeAssertion.key /r:Microsoft.Web.Services3.dll UsersManager.cs UsersManager.key: sn -k UsersManager.key install: gacutil -nologo -i Seva.WS.UsersManager.dll

На стороне сервера в файле конфигурации (web.config) следует добавить информацию о

менеджере учетных записей в элемент <microsoft.web.services3> <security>.

Page 149: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 7. Промежуточная среда веб-служб ASP.NET

144

<?xml version="1.0" encoding="utf-8"?> <configuration> ... <microsoft.web.services3> <security> <securityTokenManager> <add type="Seva.WS.Users.UsersListManager, Seva.WS.UsersManager, Version=1.0.0.0, Culture=neutral, PublicKeyToken=..." namespace= "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" localName="UsernameToken"> <users file="C:\Inetpub\users.config"/> </add> </securityTokenManager> </security> <policy fileName="wse3policyCache.config" /> </microsoft.web.services3> </configuration>

Кроме процедуры установления идентичности пользователя, может существовать

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

разделе <policy><authorization> политики веб-службы можно организовать список

пользователей, используя элементы <allow> и <deny>.

Для посылки клиентом хеша пароля может использоваться описанное далее

расширение UsernameClientAssertion. Оно может быть использовано для не

представляющей критической важности веб-службы, не использующей сертификаты

X.509. Использование выданных или «самодельных» серверных сертификатов X.509 и

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

большую степень безопасности.

[XmlRoot("user")] public struct UserCredential { [XmlAttribute("username")] public string Username; [XmlAttribute("password")] public string Password; } public class UsernameClientAssertion: SecurityPolicyAssertion { private UserCredential credential; public UsernameClientAssertion() { }

Метод ReadXml считывает из файла параметры политики, состоящие из имени файла

с именем пользователя и паролем. Хранение паролей в файле должно как минимум

Page 150: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 7. Промежуточная среда веб-служб ASP.NET

145

сочетаться с защитой данного файла средствами операционной системы. Однако

задание пароля в программном коде (например, после ввода его пользователем с

клавиатуры) невозможно при использовании веб-службы любой не интерактивной

программной компонентой.

public override void ReadXml(XmlReader reader, IDictionary<string, Type> extensions) { string fileName = reader.GetAttribute("file"); if (fileName == null) throw new Exception("Attribute 'file' not found in policy"); // Обязательная операция - переход к следующему тегу в файле политики reader.Read(); using (StreamReader stream = new StreamReader(fileName)) { XmlSerializer serializer = new XmlSerializer(typeof(UserCredential)); using (XmlTextReader xmlReader = new XmlTextReader(stream)) { credential = (UserCredential) serializer.Deserialize(xmlReader); } } }

Метод CreateClientOutputFilter данного расширения создает фильтр SOAP, который

будет добавлять информацию о пользователе в заголовок сообщения.

public override SoapFilter CreateClientOutputFilter( FilterCreationContext context) { return new UsernameClientFilter(this, credential, context); } public override SoapFilter CreateClientInputFilter( FilterCreationContext context) { return null; } public override SoapFilter CreateServiceInputFilter( FilterCreationContext context) { return null; } public override SoapFilter CreateServiceOutputFilter( FilterCreationContext context) { return null; } }

Page 151: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 7. Промежуточная среда веб-служб ASP.NET

146

Фильтр наследован от класса SendSecurityFilter и добавляет в сообщение токен с

паролем в форме хеша в своем методе SecureMessage.

class UsernameClientFilter : SendSecurityFilter { private UserCredential credential; public UsernameClientFilter(UsernameClientAssertion assertion, UserCredential credential, FilterCreationContext filterContext) : base(assertion.ServiceActor, true, assertion.ClientActor) { this.credential = credential; } public override void SecureMessage(SoapEnvelope envelope, Security security) { UsernameToken userToken = new UsernameToken(credential.Username, credential.Password, PasswordOption.SendHashed); security.Tokens.Add(userToken); } } }

Для составления файла с пользователями можно использовать следующую

вспомогательную программу.

// AddUser.cs using System; using System.IO; using Seva.WS.Users; class MainApp { public static int Main(string[] Args) { if (Args.Length < 3) { Console.WriteLine( "usage: adduser <users' file> <username> <password> [--hashed]"); return 1; } UsersList users = new UsersList(); string fileName = Args[0]; if (File.Exists(fileName)) { users = UsersList.Load(fileName); } bool hashed = false; if (Args.Length==4) { hashed = Args[3]=="--hashed"; }

Page 152: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 7. Промежуточная среда веб-служб ASP.NET

147

users.NewUser(Args[1], Args[2], hashed); users.Save(fileName); return 0; } }

На стороне сервера можно воспользоваться стандартным расширением WSE

UsernameOverTransportAssertion без шифрования сообщения, которое будет

использовать описанный ранее специальный менеджер пользовательских записей,

простейший вариант файла политики wse3policyCache.config указан ниже.

<policies xmlns="http://schemas.microsoft.com/wse/2005/06/policy"> <extensions> <extension name="usernameOverTransportSecurity" type="Microsoft.Web.Services3.Design.UsernameOverTransportAssertion, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> <extension name="requireActionHeader" type="Microsoft.Web.Services3.Design.RequireActionHeaderAssertion, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> </extensions> <policy name="ServicePolicy"> <usernameOverTransportSecurity /> <requireActionHeader /> </policy> </policies>

Файл политики клиента при использовании расширения UsernameClientAssertion имеет

следующий вид.

<policies xmlns="http://schemas.microsoft.com/wse/2005/06/policy"> <extensions> <extension name="simpleUserName" type="Seva.WS.Users.UsernameClientAssertion, Seva.WS.UsersManager, Version=1.0.0.0, Culture=neutral, PublicKeyToken=..."/> </extensions> <policy name="ClientPolicy"> <simpleUserName file="user.xml"/> </policy> </policies>

Файл user.xml содержит имя пользователя и пароль клиента веб-службы.

<user username="user2" password="222" />

При использовании шифрования сообщений, например, на основе сертификатов X.509

и расширения UsernameForCertificateAssertion использовать описанное расширение

клиента UsernameClientAssertion не следует, поскольку при шифровании всего

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

можно использовать расширение стандартное UsernameForCertificateAssertion для

Page 153: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 7. Промежуточная среда веб-служб ASP.NET

148

клиента и сервера, которое будет использовать созданный менеджер пользователей.

Наиболее простой способ конфигурирования политик в таком случае заключается в

использование утилиты WseConfigEditor3.exe.

7.6. Выводы по использованию веб-служб

Веб-службы являются принятым индустриальным стандартом взаимодействия в

распределенных системах. Их реализация на платформе .NET позволяет при

использовании WSE полностью отделить политику доступа к службе от самого

механизма службы. Проанализируем веб-службы с точки зрения требований к

распределенной системе.

1. Открытость. Веб-сервисы построены на общепринятых стандартах и в силу этого

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

любых сетях TCP/IP, в том числе через NAT и межсетевые экраны, поскольку обычно

используют стандартный порт протокола HTTP. При необходимости веб-службы могут

быть использованы поверх различных транспортных протоколов.

2. Масштабируемость, и устойчивость. Веб-службы поддерживают только модель

единственного вызова, поэтому балансировка нагрузок и обеспечение надежности

сервиса может быть организовано путем создания кластера IIS или иными способами,

оперирующими только с транспортным протоколом веб-служб. Модель единственного

вызова позволяет создавать масштабируемые приложения.

3. Безопасность. Поддерживаемый WSE стандарт WS-Security 1.1 позволяет

организовать разнообразные способы аутентификации пользователя, совмещенные с

шифрованием сообщения и организации списка доступа к сервису.

4. Поддержание логической целостности данных. Недостатком текущей реализации

веб-служб является отсутствие поддержки в настоящий момент распределенных между

несколькими веб-службами транзакций. В случае веб-служб .NET данная проблема

будет, вероятно, решена по мере перехода на .NET Framework 3.0 и WCF.

Page 154: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 7. Промежуточная среда веб-служб ASP.NET

149

5. Эффективность (в узком смысле). На передачу и разбор сообщений SOAP тратятся

некоторые временные ресурсы, однако это является умеренной платой за открытый и

расширяемый характер веб-служб.

Все сказанное выше позволяет считать веб-сервисы наиболее универсальной, открытой,

масштабируемой и надежной технологией построения распределенных систем.

Реализация механизма расширений WSE достаточно понятна и проста для

использования разработчиком. После повсеместного принятия полного набора

стандартов WS-* веб-службы будут обладать широкими возможностями поддержки

распределенных транзакций. Также определенным недостатком веб-служб ASP.NET

можно считать отсутствие в настоящий момент поддержки локального взаимодействия,

не затрагивающего стек TCP/IP.

Page 155: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

150

Тема 8. Промежуточная среда .NET Remoting

Рассматривается промежуточная среда .NET Remoting, примеры ее использования и

модификации архитектуры данной среды. Даются рекомендации по применению

.NET Remoting в распределенных приложениях.

8.1. Введение в среду .NET Remoting

В отличие от других промежуточных сред, рассматриваемых в данном курсе, среда

.NET Remoting создавалась специально для платформы .NET. Среда Remoting является

универсальным средством доступа к удаленным объектам, которое может быть

приспособлено к широкому классу задач взаимодействия компонент распределенного

приложения. Благодаря своей расширяемой архитектуре среда Remoting может быть

доработана для использования с практически любыми каналами передачи данных. Уже

со штатными средствами область применения среды Remoting охватывает как

реализацию публичных веб-служб в интернете на основе протокола SOAP, так и

высокоскоростной обмен в доверенной сети на основе бинарного

форматирования (рис. 8.1).

Сервер.NET Remoting

Объекты

Межсетевой экран

HTTP / SOAP

Интернет

HTTP / SOAP

Локальнаясеть

Клиент .NETRemoting

Клиент .NETRemotingSOAP-клиент

TCP / бинарноеформатирование

ОбъектыОбъекты

Рисунок 8.1. Различные сценарии использования среды Remoting

Page 156: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

151

Кроме использования в качестве самостоятельной промежуточной среды, .NET

Remoting также используется для организации взаимодействия .NET Framework и

среды COM+ в рассмотренной ранее промежуточной среде .NET Enterprise Services.

С точки зрения среды Remoting, все классы объектов среды CLR делятся на три вида.

1. Классы, маршализируемые по значению. Объекты этих классов могут копироваться

между доменами приложений, если для них определены операции сериализации и

десериализации. В результате десериализации создается копия объекта, не связанная с

его оригиналом. Следует отметить, что сериализация и десериализация некоторых

объектов может быть осуществлена не со всеми классами форматирования, как указано

в теме о сериализации.

2. Классы, маршализируемые по ссылке. Такие классы наследуются от класса

System.MarshalByRefObject. Объекты этих классов не покидают свой домен

приложения, но на стороне клиента создается посредник, позволяющий осуществлять

удаленный доступ к объекту. Именно экземпляры таких классов могут использоваться

как удаленные объекты при помощи среды Remoting. Remoting поддерживает все три

вида удаленных объектов: объекты единственного вызова, единственного экземпляра и

объекты, активируемые по запросу клиента.

3. Немаршализируемые классы, а так же классы без определенной процедуры

сериализации или отмеченные, как несериализуемые. Объекты этих классов

недоступны вне своего домена приложения. Это, в частности, касается и классов

исключений – выбрасываемые на сервере Remoting исключения должны

маршализироваться по значению для передачи клиенту.

Таким образом, среда Remoting является средством поддержки удаленных вызовов

между доменами приложений CLR. В отличие от технологий MSMQ и COM+, среда

Remoting не привязана жестко к каким-либо службам операционной системы Microsoft

Windows. Благодаря этому ответственные за реализацию среды Remoting пространства

имен являются стандартизированной частью CLI, в отличие от пространств

System.EnterpriseServices и System.Messaging. Таким образом, одним из достоинств

Remoting является переносимость как между различными реализациями CLI фирмы

Page 157: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

152

Microsoft (.NET Framework, .NET Compact Framework), а также независимыми

реализациями CLI (в настоящий момент к ним относятся Mono Project и DotGNU

Portable.NET).

Реализация промежуточной среды Remoting только силами исполняемой среды CLI

привела и к определенным недостаткам. Возможности Remoting как промежуточной

среды достаточно скромны по сравнению со средой Enterprise Services. Remoting не

имеет аналогов большинства сервисов среды COM+, в первую очередь это касается

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

средства обеспечения безопасности, однако эта проблема может быть решена

сервером-носителем среды Remoting, в роли которого может выступать служба Internet

Information Services (IIS).

8.2. Архитектура среды .NET Remoting

Среда Remoting имеет достаточно сложную организацию удаленного вызова, которая

позволяет разработчику при необходимости полностью контролировать и

модифицировать процесс вызова клиентом Remoting метода объекта сервера. Это, с

одной стороны, дает возможность после доработок применять среду Remoting

практически с любыми каналами передачи информации (например, поверх MSMQ) или

для некоторых специальных целей (например, для взаимодействия CLR и COM+).

Открытая и изменяемая архитектура среды также делает ее достаточно

привлекательной с педагогической точки зрения при изучении механизмов удаленного

вызова. С другой стороны, модификация архитектуры Remoting является достаточно

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

нестандартные и недостаточно проверенные, в том числе с точки зрения безопасности,

расширения промежуточной среды. Поэтому вопрос о полезности больших

возможностей по расширению промежуточной среды конечным разработчиком

остается, видимо, открытым.

Page 158: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

153

Клиент .Net Remoting

Клиентский канал.Net Remoting

Сервер .Net Remoting

Вызывающаяфункция ПосредникиВызов

метода

Серверный канал.Net Remoting

Маршализируемыйпо ссылке объект

Сообщениес информациейо вызове метода

Цепочка трубсообщения

Трубаформатирования

Сообщение в видепотока ввода-вывода

Цепочкатруб потока

Транспортнаятруба

Сообщение в видепотока ввода-вывода

Канал передачи данных

Трубадиспетчеризации

Вызовметода

Транспортнаятруба

Цепочкатруб потока

Сообщение в видепотока ввода-вывода

Трубаформатирования

Сообщениео вызове метода

Сообщениео вызове метода

Сообщение в видепотока ввода-вывода

Цепочка трубсообщения

Сообщениео вызове метода

Рисунок 8.2. Выполнения удаленного вызова в .NET Remoting

Архитектура среды Remoting (рис. 8.2) включает в себя следующие основные

сущности, реализуемые классами из пространства имен System.Runtime.Remoting и его

подпространств.

1. Посредники удаленного объекта. В среде используется два посредника,

принадлежащие классам TransparentProxy и RealProxy. Первый из них является

Page 159: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

154

классическим посредником и реализует интерфейс удаленного объекта. Второй,

«настоящий», посредник получает от «прозрачного» посредника сообщение об

удаленном вызове (класс IMessage). «Настоящий» посредник может быть изменен

разработчиком для реализации каких-то специфических функций, и поэтому посредник

удаленного объекта в среде Remoting представлен двумя сущностями.

2. Сообщение проходит по каналу (channel) среды Remoting. Канал состоит из

отдельных труб (sinks) и может иметь различную структуру. Как минимум, клиентская

сторона канала включает трубу форматирования, преобразующую сообщение об

удаленном вызове в поток ввода-вывода, и транспортную трубу, передающую данный

поток в канал передачи данных. Серверная сторона канала, в свою очередь, состоит как

минимум из транспортной трубы, трубы форматирования и трубы диспетчеризации.

Трубы форматирования и транспорта присутствуют на каждой стороне не более чем в

одном экземпляре. Канал может содержать и другие трубы, которые оперируют с

сообщением или полученным из него потоком. В первом случае трубы располагаются

до трубы форматирования (на клиенте) и реализуют интерфейс IMessageSink. Такие

трубы называют трубами сообщения (message sinks). Во втором случае трубы

располагаются между трубами форматирования и транспорта, и называются трубами

обработки потока (channel sinks). Они реализуют интерфейс IСlientChannelSink на

клиенте или IServerChannelSink на сервере. Стандартная труба форматирования

реализует интерфейсы и трубы потока, и трубы сообщения, а транспортная труба

относится к трубам потока.

3. Последняя из труб в серверной части канала должна передать сообщение заглушке

удаленного вызова, называемой диспетчером (реализуется методом

ChannelServices.DispatchMessage) .

4. Конкретный набор труб, образующий канал Remoting, определяется используемым

классом канала. Такой класс реализует интерфейсы IChannel, IChannelSender,

IChannelReceiver и должен создавать последовательность труб на стороне клиента

и/или сервера, как это будет показано далее в примерах. Среда Remoting сразу

«из коробки» содержит три класса канала. Классы HttpChannel и TcpChannel работают с

протоколами HTTP и TCP соответственно, а добавленный в .NET Framework 2.0 класс

Page 160: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

155

IpcChannel предназначен для эффективного локального взаимодействия процессов

(IPC: inter-process communication) через именованные каналы. В трубе форматирования

любого из трех каналов может использоваться один из двух рассмотренных ранее

классов форматирования: BinaryFormatter и SoapFormatter.

Для передачи информации об объекте сервера и создания на клиенте «прозрачного»

посредника используется особый класс System.Runtime.Remoting.ObjRef. При попытке

сериализации и передачи по каналу маршализируемого по ссылке объекта используется

рассмотренный ранее механизм сурогатных селекторов. Благодаря этому механизму

при сериализации наследников класса System.MarshalByRefObject по каналу Remoting

вместо них передается маршализируемый по значению объект класса ObjRef, который

после десериализации становится «прозрачным» посредником на клиенте Remoting

(рис. 8.3).

Клиент.NET Remoting

Сервер .NETRemoting

Объект

КаналRemoting

Сериализованный объекткласса ObjRef

КаналRemoting

Посредник

Рисунок 8.3. Маршализация по ссылке в среде Remoting

Таким образом, среда Remoting позволяет организовать синхронный или

односторонний удаленный вызов методов объектов, наследованных от класса

MarshalByRefObject. Асинхронный односторонний вызов осуществляется для методов

Page 161: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

156

с атрибутом OneWayAttribute2. Такие методы не должны возвращать какие-либо

результаты, и вызываются по принципу «выстрелил и забыл». Выбрасываемые в ходе

его выполнения исключения также не передаются клиенту. Среда Remoting

поддерживает все три вида активации объектов, причем для объектов единственного

вызова изменение значения свойств и общедоступных полей рассматривается как

удаленный вызов, за которым следует освобождение объекта. Среда Remoting не

поддерживает пул объектов.

Одной из проблем при использовании распределенных объектов является определение

момента их уничтожения сервером. Для автоматического удаления из памяти

локальных объектов, используемых удаленным клиентом, недостаточно отслеживания

локальных ссылок на них. В среде Remoting для управлением временем жизни таких

объектов используется система аренды (lease), которая при использовании

активируемых клиентом объектов или объектов единственного экземпляра с

некоторым интервалом просит клиента подтвердить необходимость существования

объекта. Если на стороне клиента уже нет активных ссылок на удаленный объект, то

подтверждения не происходит, и при отсутствии хотя бы одного подтверждения объект

на сервере будет освобожден. Для объектов единственного вызова в такой системе нет

нужды, поскольку объекты создаются сервером при удаленном вызове и

освобождаются сервером после завершения любого удаленного вызова (включая

методы set и get свойств). В силу этого с объектами, активируемыми сервером, может

использоваться только конструктор по умолчанию, не имеющий параметров.

8.3. Конфигурирование среды .NET Remoting

Среда Remoting имеет штатную возможность описания своей конфигурации в

XML-файле. К конфигурации среды Remoting относятся:

– используемый класс канала;

– параметры канала (например, используемый сервером порт TCP);

2 При помощи делегатов и BeginInvoke можно асинхронно вызвать любой метод, в данном случае

подразумевается асинхронность с точки зрения промежуточной среды.

Page 162: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

157

– используемый класс форматирования;

– адрес и тип используемого удаленного объекта.

Разработчик может задать конфигурацию среды Remoting непосредственно в

программе, но данный метод рассматриваться не будет. Применение файлов

конфигураций позволяет отделить используемый в программе удаленный объект от

места его физического размещения, и допускает настройку одного и того же

распределенного приложения на использование различных каналов передачи данных

без изменения исходного кода программы. Данная возможность должна всегда

использоваться разработчиками, поэтому далее будет рассматриваться использование

среды Remoting только с файлами конфигурации. После корректного

конфигурирования среда Remoting обеспечивает прозрачное использование удаленных

объектов (рис. 8.4).

Клиент .NET RemotingСервер .NET Remoting

RemoteWorker worker = new RemoteWorker();results = worker.DoWork();

RemoteReport report = new RemoteReport();report.ReportResults(results);

RemoteWorker

Сервер .NET Remoting

RemoteWorker

Конфигурация.Net Remoting

Конфигурация.Net Remoting

Конфигурация.Net Remoting

Рисунок 8.4. Использование конфигурации Remoting

Файл конфигурации на стороне, где находится маршализируемый по ссылке объект,

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

приложения может выступать либо IIS, либо пользовательская программа, обычно

реализованная как сервис операционной системы. Для учебных целей можно

воспользоваться следующим сервером .NET Remoting.

Page 163: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

158

// server.cs using System; using System.Runtime.Remoting; using System.Configuration; public class Server { public static void Main(string[] args) { string config = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile; Console.WriteLine(config); RemotingConfiguration.Configure(s); // Для .NET Framework 2.0 рекомендуется использовать // RemotingConfiguration.Configure(s, false); Console.WriteLine("Нажмите ENTER для завершения работы сервера."); Console.ReadLine(); } }

Как видно из приведенного примера, назначение такого сервера – настройка среды

Remoting на основе содержимого стандартного конфигурационного файла программы.

Не претендуя на полное изложение всех возможностей конфигурационного файла

среды Remoting, рассмотрим следующий пример конфигурации сервера.

<configuration> <system.runtime.remoting> <application> <service> <wellknown mode="SingleCall" type="RemoteTest.TestService, RemoteTestService" objectUri="endpoint"/> </service> <channels> <channel ref="tcp" port="2080" /> </channels> </application> </system.runtime.remoting> </configuration>

В разделе <application> <service> указываются публикуемые сервером объекты. Для

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

краткое имя сборки, которая содержит класс), и уникальный URI, связываемый с

данным классом. Объекты, активируемые сервером, описываются как

<wellknown mode="SingleCall" type=...

для режима единственного вызова или

<wellknown mode="Singletone" type=...

для режима одного экземпляра. Объекты, активируемые по запросу клиента,

описываются иначе.

<activated type=...

Page 164: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

159

Раздел <application><channels> сервера в простейшем случай содержит

идентификатор канала и номер порта. Стандартные каналы имеют идентификаторы,

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

использоваться модифицированные или вновь созданные каналы, как будут показано

далее.

Файл конфигурации клиента обычно выглядит примерно следующим образом.

<configuration> <system.runtime.remoting> <application> <client> <wellknown mode="SingleCall" type="RemoteTest.TestService, RemoteTestService" objectUri="tcp://127.0.0.1:2080/endpoint" /> </client > </application> </system.runtime.remoting> </configuration>

Раздел <application><client> включает описание используемых удаленных объектов.

Следует отметить, что один и тот же файл может содержать и разделы <service>, и

<client>. Программа при этом будет настроена как сервер и клиент Remoting

одновременно.

Клиент, использующий удаленный объект, сначала должен настроить среду Remoting.

После чего при создании объекта любого из перечисленных в файле конфигурации в

разделе <client> классов будет создан посредник, связанный с соответствующим

удаленным объектом.

using System; using System.Runtime.Remoting; using RemotingTest; public class Client { public static void Main(string[] args) { string config = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile; Console.WriteLine(config); RemotingConfiguration.Configure(config); // Для объекта единственого вызова в следующей строчке будут // созданы посредники, конструктор самого объекта не вызывается TestService service = new RemotingTest.TestService();

Page 165: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

160

// При удаленном вызове на сервере будет сконструирован новый объект Console.WriteLine(service.Sum(2, 2)); } }

Ниже приведен код самого маршализируемого по ссылке объекта. Конструктор и

деструктор объекта выводят сообщения в консоль, чтобы продемонстрировать

моменты создания и удаления объекта на сервере.

// TestService.cs using System; namespace RemotingTest { public class TestService : MarshalByRefObject { public TestService() { Console.WriteLine("[ctor]"); } ~TestService() { Console.WriteLine("[dtor]"); } public int Sum(int a, int b) { return a + b; } } }

Нижеследующий make-файл позволяет собрать все приведенные выше файлы. Следует

отметить, что в некоторых реализациях CLI для компиляции следует использовать

ссылку на сборку System.Runtime.Remoting.dll. В .NET Framework 2.0 эта ссылка

добавлена в стандартный файл параметров компилятора C# (csc.rsp).

makefile : Server.exe Client.exe Server.exe : Server.cs csc Server.cs Client.exe : Client.cs RemoteTestService.dll csc /r:RemoteTestService.dll Client.cs RemoteTestService.dll: TestService.cs csc /out:RemoteTestService.dll /t:library TestService.cs

8.4. Веб-службы в .NET Remoting

После конфигурирования среда Remoting обеспечивает прозрачное использование

удаленных объектов. Однако, для такого прозрачного использования клиент должен

Page 166: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

161

иметь доступ к сборке с самим объектом, сборка с его интерфейсом недостаточна. Для

решения данной проблемы можно либо пожертвовать прозрачностью,

воспользовавшись классом System.Activator, или организовать веб-службу Remoting на

основе SOAP, что позволяет создать посредника на клиенте по спецификации

веб-службы в формате WSDL. Вариант с веб-службой представляется более разумным

– если клиент не имеет доступа к сборке удаленного объекта, то, вероятно, клиент и

сервер находятся на разных предприятиях, и видимо речь идет о взаимодействии вне

доверенной сети.

При использовании класса форматирования SoapFormatter, канала HttpChannel и

активируемых сервером объектов среда Remoting позволяет организовать веб-службу

на основе SOAP, WSDL и кодирования SOAP-RPC (а не SOAP-Document, в отличие от

служб ASP.NET). В этом случае нет необходимости иметь сборку на клиенте,

поскольку, используя методы класса

System.Runtime.Remoting.MetadataServices.MetaData, можно получить сборку из

описания веб-службы. Для упрощения данной задачи существует утилита soapsuds.exe.

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

является наследником System.Runtime.Remoting.Services.RemotingClientProxy, имеет

интерфейс удаленного объекта и может быть использован для сборки клиента Remotig.

soapsuds.exe -url:http://localhost:2080/endpoint?wsdl -gc

Следует отметить, что в файле конфигурации клиента по-прежнему должно быть

указано имя сборки на сервере (оно совпадает с именем файла, созданного утилитой

soapsuds.exe). Созданный утилитой soapsuds.exe посредник не будет использоваться в

ходе выполнения программы, если используется конфигурация Remoting. Если же

клиент не использует метод RemotingConfiguration.Configure, то будет использован

посредник, который попытается использовать тот же сервис, который был указан при

его создании в его конструкторе. Поскольку служба Remoting создает стандартную

веб-службу, то можно воспользоваться и описанной ранее утилитой wsdl.exe для

создания клиента веб-службы.

Важной особенностью среды Remoting является возможность использования в качестве

сервера IIS, при этом клиент должен использовать канал HTTP. Конфигурация сервера

Page 167: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

162

в этом случае храниться в файле web.config в каталоге виртуального сервера, а в его

подкаталоге bin размещаются сборки с маршализируемыми по ссылке объектами.

Среда Remoting не содержит штатных средств обеспечения безопасности, и

единственным практическим способом решения данной проблемы является именно

использование IIS. Это позволяет использовать авторизацию и защиту передаваемых

данных с помощью SSL. При использовании SSL, кроме соответствующей настройки

сервера, клиент должен использовать префикс объектов https://.

Для идентификации пользователя при использовании встроенной авторизации IIS

можно воспользоваться примерно следующим кодом.

RemoteObject object = new RemoteObject(); // Параметры идентификации клиента IDictionary properties = ChannelServices.GetChannelSinkProperties(object); properties["username"] = username; properties["password"] = password; // Удаленный вызов object.Do();

При использовании IIS можно применять декларативную безопасность с помощью

атрибута PrincipalPermissionAttribute, или императивную безопасность, когда метод

объекта проверяет, принадлежит ли авторизованный пользователь к той или иной

группе пользователей.

IPrincipal principal = System.Threading.Thread.CurrentPrincipal; if (!principal.IsInRole(Environment.MachineName + @"\SomeGroup")) { throw new UnauthorizedAccessException("Нет прав доступа."); }

При использовании в пределах домена или группы дружественных доменов Active

Directory служба IIS позволяет использовать авторизацию операционной системы, для

чего клиенту достаточно указать в файле конфигурации примерно следующее.

<channel ref="http" useDefaultCredentials="true" />

Таким образом, при использовании внутри Active Directory вместе со службой IIS среда

Remoting позволяет легко создать безопасную систему на основе авторизации Windows

(протокол Kerberos), сертификатов сервера и ролей пользователей. При использовании

Page 168: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

163

вне Active Directory вместо идентификации Windows можно использовать простую

идентификацию.

8.5. Канал среды Remoting

Канал, по которому в среде Remoting передается сообщение удаленного вызова,

образуется списком труб, который так же называется цепочкой (chain). Каждая труба в

цепочке клиентской части канала, за исключением транспортной, содержит ссылку на

следующую трубу. Эта ссылка содержится в свойстве NextSink (для интерфейса

IMessageSink) или NextChannelSink (для интерфейсов IClientChannelSink и

IServerChannelSink).

Клиент .Net RemotingЦепочка труб

клиентской частиканала

Трубысообщения

Трубаформатирования

Трубы потока

Транспортнаятруба

Цепочкапоставшиковтруб

Канал

Поставщик трубыформатирования

Поставщиктранспортной трубы

Поставщикитруб потока

Рисунок 8.5. Поставщики труб канала

Цепочка труб создается цепочкой так называемых поставщиков труб (sink providers).

Каждый поставщик, реализующий интерфейс IClientChannelSinkProvider или

IServerChannelSinkProvider, создает цепочку из одной или нескольких труб в методы

Page 169: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

164

CreateSink (рис. 8.5). В отличие от труб канала, трубы сообщения (интерфейс

IMessageSink) создаются не поставщиками, а самим каналом в методе

CreateMessageSink.

Канал на стороне сервера устроен подобным образом, за исключением того, что он

должен заканчиваться трубой диспетчеризации. Для этого обычно вызывается метод

ChannelServices.CreateServerChannelSinkChain, который добавляет к цепочке

поставщиков стандартного поставщика трубы диспетчеризации

DispatchChannelSinkProvider.

Обычно каждый поставщик создает цепочку из единственной трубы, что позволяет

изменять свойства канала, добавляя или заменяя только часть поставщиков.

Поставщики также образуют список, использующий их свойство Next. Стандартные

каналы включают поставщика трубы форматирования и поставщика транспортной

трубы. Например, цепочка поставщиков канала TcpClientChannel (клиентской части

TcpChannel) состоит по умолчанию из поставщиков BinaryClientFormatterSinkProvider и

TcpClientTransportSinkProvider. При этом поставщик форматирования может быть задан

в конфигурационном файле. В простейшем случае достаточно указать ссылку на

зарегистрированный тип в разделе <channel><serverProviders><formatter> в

конфигурации сервера и в разделе <channel><clientProviders><formatter> на клиенте.

Для стандартных классов форматирования это ссылки binary или soap.

<configuration> <system.runtime.remoting> <application> <service> <wellknown mode="SingleCall" type="RemotingTest.TestService, RemoteTestService" objectUri="endpoint"/> </service> <channels> <channel ref="http" port="2080"> <serverProviders> <formatter ref="binary"/> </serverProviders> </channel> </channels> </application> </system.runtime.remoting> </configuration>

Page 170: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

165

В качестве примера модификации рассмотрим добавление шифрования в стандартный

канал на основе симметричного шифрования. Симметричное шифрования выбрано в

данном примере исключительно из-за простоты реализации. На практике следовало бы

скорее использовать для данной цели шифрование с открытом ключом, однако

действительно правильным решением обеспечения безопасности передачи данных в

Remoting заключается либо в использовании IIS, либо в развертывании VPN на основе

безопасного решения (например, OpenVPN). Поэтому данную модификацию канала

следует рассматривать исключительно как пример по модернизации инфраструктуры

.NET Remoting.

Для реализации шифрования достаточно добавить в цепочку поставщиков труб потока

дополнительного поставщика, который будет создавать трубу, шифрующую и

дешифрующую проходящие по ней потоки с сериализованными сообщениями. Для

добавления такого поставщика в канал следует использовать файл конфигурации. Для

использовании стандартного канала с дополнительной трубой достаточно указать имя

класса поставщика трубы в разделе <channel><clientProviders><provider>.

В нижеследующем файле описаны классы поставщика трубы клиента и самой трубы, а

также поставщика трубы сервера и его трубы. Сам механизм шифрования на основе

стандартных классов FCL описан в приложении III.

// Файл SevaRemotingEncrypted.cs using System; using System.IO; using System.Collections; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using System.Runtime.Remoting.Messaging; using Seva.Security.Encryption; namespace Seva.Remoting.Encryption { class EncryptedClientChannelSinkProvider: IClientChannelSinkProvider { private IClientChannelSinkProvider next; public IClientChannelSinkProvider Next { get { return next; } set { next = value; } } private SymmetricEncryptor encryptor;

Page 171: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

166

Конструктор поставщика трубы на стороне клиента получает в качестве аргумента

словарь свойств, заполненный свойствами из файла конфигурации. Это позволяет

указывать имя файла с ключом шифрования в файле конфигурации, а в конструкторе

создается симметричный шифровальщик с указанным ключом.

public EncryptedClientChannelSinkProvider(IDictionary properties, ICollection providerData) { string keyFile = (string) properties["key"]; Console.WriteLine("Client key: [{0}]", keyFile); encryptor = new SymmetricEncryptor(keyFile); }

Метод CreateSink является основным методом поставщика трубы. В типичном случае

сначала вызывается этот же метод для следующего в цепочке поставщика, а затем

создается труба, вставляемая в цепочку труб. Следует отметить, что данный поставщик

не может быть последним в цепочке, но соответствующие проверки свойства Next и

выбросы исключений для экономии места не показаны.

public IClientChannelSink CreateSink(IChannelSender channel, string url, object remoteChannelData) { IClientChannelSink next = Next.CreateSink(channel, url, remoteChannelData); return new EncryptedClientChannelSink(encryptor, next); } }

Класс поставщика трубы на стороне сервера устроен аналогичным образом.

class EncryptedServerChannelSinkProvider : IServerChannelSinkProvider { private IServerChannelSinkProvider next; private SymmetricEncryptor encryptor; public IServerChannelSinkProvider Next { get { return next; } set { next = value; } } public EncryptedServerChannelSinkProvider(IDictionary properties, ICollection providerData) { string keyFile = (string) properties["key"]; Console.WriteLine("Server key: [{0}]", keyFile); encryptor = new SymmetricEncryptor(keyFile); }

Page 172: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

167

// Создание трубы канала public IServerChannelSink CreateSink(IChannelReceiver channel) { IServerChannelSink nextSink = Next.CreateSink(channel); return new EncryptedServerChannelSink(channel, encryptor, nextSink); } // Обязательный метод интерфейса public void GetChannelData(IChannelDataStore channelData) { } }

Собственно шифрование выполняется в созданными поставщиками трубах канала на

стороне клиента и сервера.

class EncryptedServerChannelSink : IServerChannelSink { public IDictionary Properties { get { return null; } } private IServerChannelSink nextSink; public IServerChannelSink NextChannelSink { get { return nextSink; } } private SymmetricEncryptor encryptor; public EncryptedServerChannelSink(IChannelReceiver channel, SymmetricEncryptor channelEncryptor, IServerChannelSink next) { encryptor = channelEncryptor; nextSink = next; }

Главным методом трубы при синхронном удаленном вызове является метод

ProcessMessage. В случае трубы потока данный метод может оперировать как с самим

сообщением, так и с полученным из него в ходе сериализации потоком. Труба

шифрования сервера должна дешифровать этот поток при получении сообщения от

клиента и зашифровать поток при посылке клиенту ответа сервера.

// Метод синхронной обработки сообщения public ServerProcessing ProcessMessage(IServerChannelSinkStack sinkStack, IMessage requestMsg, ITransportHeaders requestHeaders, Stream requestStream, out IMessage responseMsg, out ITransportHeaders responseHeaders, out Stream responseStream) { Stream plainStream = null; MemoryStream decodedStream = new MemoryStream(); requestStream = Utils.ReadAllStream(requestStream);

Page 173: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

168

// Расшифровать запрос клиента encryptor.Decrypt(requestStream, decodedStream); decodedStream.Position = 0; ServerProcessing result = nextSink.ProcessMessage(sinkStack, requestMsg, requestHeaders, decodedStream, out responseMsg, out responseHeaders, out plainStream); // Зашифровать ответ сервера responseStream = new MemoryStream(); encryptor.Encrypt(plainStream, responseStream); responseStream.Position = 0; // return result; } public void AsyncProcessResponse(IServerResponseChannelSinkStack sinkStack, Object state, IMessage msg, ITransportHeaders headers, Stream stream) { throw new NotSupportedException(); } public Stream GetResponseStream(IServerResponseChannelSinkStack sinkStack, Object state, IMessage msg, ITransportHeaders headers) { return nextSink.GetResponseStream(sinkStack, state, msg, headers); } } // EncryptedServerChannelSink

Труба шифрования клиента должна зашифровать этот поток перед отправкой на сервер

и расшифровать при получении ответа сервера.

class EncryptedClientChannelSink: IClientChannelSink { public IDictionary Properties { get { return null; } } private IClientChannelSink nextSink; public IClientChannelSink NextChannelSink { get { return nextSink; } } private SymmetricEncryptor encryptor; public EncryptedClientChannelSink(SymmetricEncryptor channelEncryptor, IClientChannelSink next) { encryptor = channelEncryptor; nextSink = next; }

Page 174: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

169

public void ProcessMessage(IMessage message, ITransportHeaders requestHeaders, Stream requestStream, out ITransportHeaders responseHeaders, out Stream responseStream) { MemoryStream encryptedStream = new MemoryStream(); Stream serverStream = null; // Зашифровать запрос клиента encryptor.Encrypt(requestStream, encryptedStream); encryptedStream.Position = 0; nextSink.ProcessMessage(message, requestHeaders, encryptedStream, out responseHeaders, out serverStream); serverStream = Utils.ReadAllStream(serverStream); // Расшифровать ответ сервера responseStream = new MemoryStream(); encryptor.Decrypt(serverStream, responseStream); responseStream.Position = 0; } public Stream GetRequestStream(IMessage msg, ITransportHeaders headers) { return null; } public void AsyncProcessRequest(IClientChannelSinkStack sinkStack, IMessage message, ITransportHeaders headers, Stream stream) { throw new NotSupportedException(); } public void AsyncProcessResponse( IClientResponseChannelSinkStack sinkStack, object state, ITransportHeaders headers, Stream stream) { throw new NotSupportedException(); } } // EncryptedClientChannelSink

Вспомогательный класс Utils служит для считывания всего содержимого потока, не

поддерживающего свойство Length.

public static class Utils { public static MemoryStream ReadAllStream(Stream input) { byte[] buffer = new byte [1024]; MemoryStream output = new MemoryStream(); while (true) { int read = input.Read(buffer, 0, buffer.Length); output.Write(buffer, 0, read); if (read < buffer.Length) break; } output.Position = 0; return output; } } } // SevaRemotingEncrypted.cs

Page 175: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

170

Для компиляции указанных файлов можно использовать следующий make-файл.

makefile : Client.exe SevaRemotingEncrypted.dll common = Seva*.cs Client.exe : Client.cs RemoteService.dll csc /r:RemoteService.dll Client.cs RemoteService.dll : RemoteService.cs csc /t:library RemoteService.cs SevaRemotingEncrypted.dll: $(common) csc /out:SevaRemotingEncrypted.dll /t:library $(common)

Как видно из данного файла, при компиляции клиент не ссылается на сборку с

модифицированными поставщиками труб. Регистрация данных поставщиков труб

происходит с помощью файлов конфигурации.

<configuration> <system.runtime.remoting> <application> <client> <wellknown type="RemoteService.RemoteServiceTest, RemoteService" url="tcp://localhost:10020/endpoint" /> </client> <channels> <channel ref ="tcp"> <clientProviders> <formatter ref="binary"/> <provider type="Seva.Remoting.Encryption. EncryptedClientChannelSinkProvider, SevaRemotingEncrypted" key="secret.key" /> </clientProviders> </channel> </channels> </application> </system.runtime.remoting> </configuration>

Таким образом, архитектура .NET Remoting позволяет встраивать собственные трубы в

канал Remoting, причем для этого не требуется что-либо менять в коде клиента или

сервера. Однако даже в простейшем случае реализация собственных поставщиков и

труб выглядит достаточно громоздко.

8.6. Создание нестандартного канала

Одним из недостатков стандартных каналов среды Remoting является плохая

поддержка асинхронного взаимодействия. Даже при вызове одностороннего метода

Page 176: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

171

удаленного объекта сервер и клиент Remoting должны быть запущены одновременно,

поскольку оба канала требуют функционирующего соединения по TCP/IP. Поэтому в

качестве примера полезной модификации среды Remoting можно рассмотреть создание

собственного канала на основе службы MSMQ. В отличие от стандартных каналов

TcpChannel и HttpChannel, данный канал обеспечивает асинхронный обмен между

клиентом и сервером. Ограничимся реализацией канала для одностороннего

удаленного вызова.

На рисунке 8.6. приведена схема созданного канала. Следует отметить, что поскольку

канал полностью базируется на промежуточной среде MSMQ, данное решение является

настолько же безопасным, насколько безопасна среда MSMQ.

Серверныйканал

КлиентскийканалТруба сообщения

Клиент MSMQ

Объект IMessage

Труба диспетчеризации

Объект

ПосредникОбъект IMessage

MSMQ

Объект IMessage

Сервер MSMQ

Сериализованноесообщение

Рисунок 8.6. Простейший канал Remoting, использующий MSMQ

Данный канал будет позволять асинхронный вызов методов с атрибутом

System.Runtime.Remoting.Messaging.OneWayAttribute, причем время работы клиента и

сервера может не совпадать.

Page 177: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

172

Вновь создаваемый канал состоит из двух основных классов: клиентcкой части

MsmqChannelSender, реализующей интерфейс IChannelSender, и серверной части

MsmqChannelReceiver, реализующей интерфейс IChannelReceiver. Указанные классы

используют описанные в разделе MSMQ классы MsmqClient и MsmqServer, причем

необходимо использовать бинарное форматирование сообщений MSMQ.

// Файл SevaRemotingMsmq.cs using System; using System.IO; using System.Collections; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Messaging; using System.Runtime.Serialization.Formatters.Binary; using System.Messaging; using System.Text.RegularExpressions; // использовать описанное в главе про MSMQ пространство имен // для работы c очередями сообщений using Seva.Msmq; // Набор классов для одностороннего удаленного вызова // на основе Remoting / MSMQ namespace Seva.Remoting.MsmqChannel { // константы с именами свойств сообщения public class MessageProperties { public const string Uri = "__Uri"; public const string ObjectUri = "__ObjectUri"; }

Класс MsmqBase является базовым классом для обеих частей канала. Он содержит

некоторые общие для них свойства и метод Parse, выделяющий из полного URL

удаленного объекта идентификатор объекта. URL при использовании данного канала

должен иметь следующий вид

msmq://.\Private$\remoting_queue\endpoint

где .\Private$\remoting_queue – имя очереди (в данном случае – частной, локальной), а

endpoint – идентификатор удаленного объекта.

// базовый класс для MsmqSender и MsmqReceiver public class MsmqBase: MarshalByRefObject, IChannel { // обязательные для интерфейса IChannel свойства public int ChannelPriority { get { return 1;} } public string ChannelName { get{ return "msmq";} }

Page 178: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

173

// разделяет URL на идентификаторы для объекта и канала public string Parse(string url, out string objectUri) { return Utils.ParseUrl(url, out objectUri); } }

Конструктору класса MsmqChannelSender в параметре clientSinkProvider передается

дополнительный поставщик, описанный в файле конфигурации, который следует

вставить в цепочку поставщиков. Однако в данном случае клиентский канал может

состоять из единственной цепочки сообщения, которая передает сообщение в MSMQ.

Хотя в соответствии с идеологией Remoting следовало бы создать отдельную трубу

форматирования, и отдельную – транспортную, для простоты примера можно

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

// канал для клиента public class MsmqChannelSender: MsmqBase, IChannelSender { public MsmqChannelSender(IDictionary properties, IClientChannelSinkProvider clientSinkProvider) { // поскольку в данном примере сообщение сразу направляется в MSMQ, // то дополнительные поставщики не поддерживаются if (clientSinkProvider!=null) { throw new NotSupportedException( "Дополнительные поставщики не поддерживаются."); } }

Метод CreateMessageSink, единственный в интерфейсе IChannelSender, создает трубу

сообщения.

public IMessageSink CreateMessageSink(string url, object channelData, out string objectUri) { // выделить из URL идентификатор для удаленного объекта string remoteobjectUrl = Utils.ParseUrl(url, ChannelName, out objectUri); if (remoteobjectUrl == null) return null; // создание трубы return new MsmqClientChannelSink(remoteobjectUrl); } } // Seva.Remoting.MsmqChannel.Msmq.MsmqSender

Класс MsmqClientChannelSink – транспортная труба клиента, передающая

сериализованное сообщение в MSMQ.

Page 179: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

174

// клиентская часть трубы канала public class MsmqClientChannelSink: IMessageSink { // клиент MSMQ, передающий объекты с интерфейсом IMessage // ответы данный объект получать не будет, но указать // отличный от void тип ответов необходимо private MsmqClient<IMessage, IMessage> msmqClient; // путь к очереди MSMQ и идентификатор удаленного объекта (endpoint) private string queuePath = null; private string objectUri = null;

Конструктор клиентской трубы канала должен создать клиента MSMQ для передачи

сообщений.

public MsmqClientChannelSink(string remoteObjectUri) { // выделение из URL пути к очереди и адреса объекта queuePath = Utils.ParseUrl(remoteObjectUri, out objectUri); // создание клиента MSMQ, не ожидающего ответов от сервера // и использующего бинарный класс форматирования msmqClient = new MsmqClient<IMessage, IMessage>(queuePath, null, QueueFormatter.Binary); } public IMessage SyncProcessMessage(IMessage msg) { throw new NotSupportedException( "Поддерживаются только методы с OneWayAttribute."); }

Метод AsyncProcessMessage используется для реализации асинхронного вызова. В

данном примере он должен передать сообщение Remoting клиенту MSMQ.

public IMessageCtrl AsyncProcessMessage(IMessage message, IMessageSink replySink) { // свойство сообщения c адресом удаленного объекта message.Properties[MessageProperties.ObjectUri] = objectUri; // передача сообщения клиенту MSMQ для его отсылки серверу msmqClient.Send(message); return null; } // данная труба - первая и последняя в цепочке public IMessageSink NextSink { get { return null; } } } // Seva.Remoting.MsmqChannel.MsmqClientChannelSink

Принимающая сторона состоит из канала MsmqChannelReceiver.

Page 180: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

175

public class MsmqChannelReceiver: MsmqBase, IChannelReceiver { // Класс-сервер MSMQ для приема сообщений Remoting private MsmqServer<IMessage, IMessage> msmqServer; // Серверная труба канала private IServerChannelSink sink; // Стек труб на стороне сервера private ServerChannelSinkStack stack; // необходимое для реализации интерфейса свойство public object ChannelData { get { return null; } }

Десериализация сообщения осуществляется в классе MsmqServer, поэтому цепочка

труб сервера состоит только из стандартной трубы диспетчеризации. Для вызова

метода ProcessMessage трубы диспетчеризации создается стек труб. Сервер ожидает

сообщения из очереди, указанной в файле конфигурации.

public MsmqChannelReceiver(IDictionary properties, IServerChannelSinkProvider serverSinkProvider) { // создать сервер MSMQ msmqServer = new MsmqServer<IMessage, IMessage>( properties["queue"].ToString(), QueueFormatter.Binary); msmqServer.ProcessMessage += OnReceive; if (serverSinkProvider!=null) { throw new NotSupportedException( "Поставщики не поддерживаются."); }

Поскольку начальной обработкой пришедших сообщений занимается класс

MsmqReceiver, а десериализацией – класс MsmqServer, то в конструкторе достаточно

создать стандартную трубу диспетчеризации сообщений на сервере.

sink = ChannelServices.CreateServerChannelSinkChain(null, this); // создание стека труб stack = new ServerChannelSinkStack(); stack.Push(sink, null); // Начать ожидание собщений в очереди StartListening(null); } // метод интерфейса IChannelReceiver, возваращает все URL для данного URI public virtual string[] GetUrlsForUri(string objectURI) { return new string[] {objectURI}; }

Page 181: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

176

// обработка пришедшего сообщения MSMQ private IMessage OnReceive(Object sender, IMessage request, MessageQueue queueResponse) { return ProcessMessage(request); } // методы, управляющие прослушивание канала public void StartListening(Object data) { msmqServer.BeginReceive(); } public void StopListening(Object data) { msmqServer.EndReceive(); }

Метод ProcessMessage обрабатывает пришедшее сообщение .NET Remoting. В данном

классе достаточно передать его дальше в трубу канала для передачи, в итоге,

диспетчеру сообщений Remoting.

private IMessage ProcessMessage(IMessage request) { IMessage response = null; // если в сообщении не указан URI объекта, то ничего не делать object uri = request.Properties[MessageProperties.ObjectUri]; if (uri == null) return null; string url = uri.ToString(); // Необходимо заполнить свойство сообщения __URI request.Properties[MessageProperties.Uri] = uri; // Передача сообщения в трубу канала Stream responseStream = null; ITransportHeaders responseHeaders = null; sink.ProcessMessage(stack, request, null, null, out response, out responseHeaders, out responseStream); return response; } } // Seva.Remoting.MsmqChannel.MsmqReceiver

Класс Utils содержит вспомогательные статические методы для разбора URL.

public static class Utils { // метод выделяет из URL идентификатор объекта и трубы public static string ParseUrl(string url, string channelName, out string objectUri) { objectUri = null; Regex re = new Regex(@"^" + channelName + @"\:\/\/(.+)$"); Match m = re.Match(url); if (!m.Success) return null;

Page 182: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

177

string sinkUri = m.Groups[1].Value; Utils.ParseUrl(url, out objectUri); return sinkUri; } // метод выделяет из URL идентификатор объекта и путь к очереди MSMQ public static string ParseUrl(string url, out string objectUri) { string queuePath = null; objectUri = null; Regex r = new Regex(@"^(.*)(\\|\/)(.+)$"); Match m = r.Match(url); if (m.Success) { objectUri = m.Groups[3].Value; queuePath = m.Groups[1].Value; } else { throw new Exception("Не найден идентификатор объекта в " + url); } return queuePath; } } // Seva.Remoting.MsmqChannel.Utils } // Seva.Remoting.MsmqChannel // Файл SevaRemotingMsmq.cs

Для компиляции сборки с классом канала можно использовать make-файл следующего

содержания.

makefile : SevaRemotingMsmq.dll common = SevaRemotingMsmq.cs SevaMsmq.cs SevaRemotingMsmq.dll: $(common) csc /out:SevaRemotingMsmq.dll /t:library $(common)

Для использования созданного канала необходимо описать его в файле конфигурации,

например, указав его полное имя в разделе <channel>.

<configuration> <system.runtime.remoting> <application> <client> <wellknown type="RemoteService.RemoteServiceOneWay, RemoteService" url="msmq://.\Private$\remoting_queue\service" /> </client> <channels> <channel type="Seva.Remoting.MsmqChannel.MsmqChannelSender, SevaRemotingMsmq"/> </channels> </application> </system.runtime.remoting> </configuration>

Page 183: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

178

Пример файла конфигурации сервера.

<configuration> <system.runtime.remoting> <application name="JobServer"> <service> <wellknown mode="SingleCall" type="RemoteService.RemoteServiceOneWay, RemoteService" objectUri="service" /> </service> <channels> <channel type="Seva.Remoting.MsmqChannel.MsmqChannelReceiver, SevaRemotingMsmq" queue = ".\Private$\remoting_queue"/> </channels> </application> </system.runtime.remoting> </configuration>

Как видно из приведенного примера, среда .NET Remoting имеет уникальные

возможности по модификации своей структуры разработчиком. Но даже в

рассмотренном простейшем случае такие изменения требуют как минимум глубокого

понимания структуры .NET Remoting.

8.7. Выводы по использованию .NET Remoting

Отличительной особенностью среды Remoting является возможность отделить объекты

распределенной системы от способа передачи сообщений удаленного вызова. Одно и

тоже распределенное приложение может работать с различными каналами передачи

данных и способами форматирования, включая локальный канал IpсChannel. Для смены

канала достаточно изменить только файлы конфигурации, используемые на клиентах и

серверах системы. Однако, при необходимости использования механизмов

безопасности на уровне промежуточной среды это преимущество исчезает – поскольку

выбор каналов связи ограничен возможностями IIS.

Рассмотрим среду .NET Remoting с точки зрения требований к распределенным

системам. При этом следует рассмотреть два типичных случая использования среды

Remoting – как веб-службы (далее – Remoting-Soap) и с использованием бинарного

форматирования (далее – Remoting-Binary).

1. Открытость. Среда Remoting-Binary может быть использована только в пределах

распределенного приложения, использующего CLI, причем для удобства работы

Page 184: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

179

нужно, чтобы клиент имел доступ к сборке с классом удаленного объекта. Среда

Remoting-Soap является открытой и гетерогенной, основанной на общепринятых

стандартах SOAP и WSDL.

2. Масштабируемость. Приложение, использующее .NET Remoting, являются плохо

масштабируемым, если используются активируемые клиентом объекты. Для объектов

же единственного вызова существует возможность организовать балансировку

нагрузок – при ее поддержке сетевой инфраструктурой или при использовании

нестандартного посредника.

3. Поддержание логической целостности данных. Среда Remoting не содержит

штатных средств поддержки распределенных транзакций.

4. Устойчивость. Среда Remoting не содержит средств повышения устойчивости

системы, но для объектов единственного вызова ее можно организовать на основе

сетевой инфраструктуры или изменениями в стандартных каналах или посредниках.

5. Безопасность. Remoting-Soap может использовать механизмы безопасности IIS. При

использовании вне IIS среда Remoting не предоставляет каких-либо встроенных

средств обеспечения безопасности, но может использоваться поверх безопасного

транспортного протокола при использовании VPN. Кроме того, при создании

нестандартного канала Remoting может работать с другими безопасными

транспортами, такими как MSMQ.

6. Эффективность (в узком понимании термина). По имеющимся оценкам,

Remoting-Binary является наиболее быстродействующей промежуточной средой из всех

рассмотренных в курсе.

Среда Remoting имеет гибкую архитектуру, позволяющую при необходимости

осуществлять полный контроль за процессом удаленного вызова. Однако, полный

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

небольшого числа распределенных приложений. Для большей же их части более

интересным представляется, например, поддержка отсутствующих в .NET Remoting

распределенных транзакций. С другой стороны, при использовании совместно с IIS

Page 185: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 8. Промежуточная среда .NET Remoting

180

среда Remoting не имеет заметных преимуществ перед веб-службами, причем для

веб-служб на основе .NET Remoting нет аналогов WSE. Несомненное преимущество

использования .NET Remoting вместе с IIS таким образом заключается в возможности

быстрого перехода от веб-служб HTTP к, например, локальному взаимодействию на

основе канала IpcChannel. Для этого достаточно изменить только файлы конфигурации,

используемые на клиента и сервере, и заменить IIS на программу, являющуюся

сервером .NET Remoting. Вероятным применение Remoting может быть так же

высокоскоростной обмен данными в LAN на основе TCP и двоичного форматирования,

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

доводом в пользу .NET Remoting так же может являться наличие поддержки этой среды

в сторонних реализациях CLI.

Page 186: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 9. Обеспечение безопасности распределенных систем в .NET Framework

181

Тема 9. Обеспечение безопасности распределенных систем в .NET Framework

Приводится классификация подходов к обеспечению безопасности распределенных

систем, описываются возможные решения по обеспечению безопасности для

распределенных систем, использующих .NET Framework.

9.1. Введение в обеспечение безопасности

Распределенная система представляет набор программных компонент, использующих

те или иные промежуточные среды для своего взаимодействия (рис. 9.1). Каждая

промежуточная среда использует так называемый транспортный протокол, в роли

которого может выступать:

– промежуточная среда (например, Remoting или COM+ поверх MSMQ);

– протокол верхнего уровня модели OSI (например, HTTP или HTTPS);

– протокол транспортного уровня модели OSI (например, TCP).

Транспортный протокол промежуточной среды, в свою очередь, использует протоколы

нижних уровней.

Безопасность натранспортном уровнепромежуточной среды

Безопасность на уровнесообщений

промежуточной среды

Программнаякомпонента

Промежуточнаясреда

Транспортныйпротокол

Программнаякомпонента

Промежуточнаясреда

Транспортныйпротокол

ПротоколыПротоколы

Рисунок 9.1. Обеспечение безопасности взаимодействия компонент

Page 187: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 9. Обеспечение безопасности распределенных систем в .NET Framework

182

Напомним, что обеспечение безопасного взаимодействия программных компонент

включает в себя:

– идентификацию пользователя сервисов компоненты (аутентификацию);

– защиту передаваемой информации от просмотра и изменения (шифрование и

цифровая подпись);

– ограничение доступа пользователей к сервисам компоненты в соответствии с

результатом их идентификации (авторизацию).

Указанные функции могут выполняться промежуточной средой, ее транспортным

протоколом или протоколами нижних уровней. Сами программные компонента не

должна участвовать в выполнении функций безопасности каким-либо образом. В

качестве примера рассмотрим веб-службы ASP.NET. Безопасность в них может

обеспечиваться:

– самой промежуточной средой (при использовании WSE);

– транспортным протоколом HTTPS;

– или даже безопасным IP соединением (IPSec), как при использовании веб-служб

поверх TCP, так и при использовании HTTP.

Другим примером является среда .NET Remoting. Ее безопасность может

обеспечиваться только на транспортом уровне, например при использовании HTTP или

HTTPS с IIS, MSMQ в качестве транспорта, а так протокола IPSec. При этом функция

авторизации в среде Remoting отсутствует, невозможно разграничить доступ

пользователей для методов удаленного класса.

Обеспечение функций безопасности на уровне промежуточной среды имеет

определенные преимущества.

1. Большая гибкость. Например, возможно шифровать только отдельные части

сообщения, а цифровая подпись применять к целому сообщению.

2. Переносимость. Промежуточная среда с собственной поддержкой безопасности

может работать с различными транспортами и не выдвигает каких-либо требований к

безопасности транспортного уровня.

Page 188: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 9. Обеспечение безопасности распределенных систем в .NET Framework

183

3. Возможность аудита. При использовании систем с маршрутизацией сообщений

(например, веб-служб) промежуточные маршрутизаторы сообщений могут добавлять в

него дополнительную служебную информацию.

4. Большое время защиты сообщения. Сообщение остается зашифрованным

максимально возможное время, в то время как при обеспечении безопасности

транспортом оно поступает в промежуточную среду уже в расшифрованном виде.

Однако реализация безопасности транспортом также имеет ряд достоинств.

1. Меньшие затраты времени. При идентификации на уровня транспортного

протокола решение об идентичности пользователя принимается без участия

промежуточной среды. Шифрование на уровне транспорта обычно также работает

быстрее.

2. Универсальность. Безопасность транспортного протокола позволяет защитить

любые промежуточные среды, его использующие. Не возникает проблем с реализацией

функций безопасности промежуточных сред разных производителей.

Можно сделать вывод, что обычно безопасность на уровне сообщений более

предпочтительна. Если промежуточная среда не поддерживает необходимую функцию

безопасности, и ее реализация на транспортом уровне невозможна, то не следует

реализовывать данную функцию на уровне компоненты. Вместо этого следует сменить

промежуточную среду или используемый ею транспорт на другие, удовлетворяющие

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

9.2. Механизмы обеспечения безопасности

Электронные сертификаты

В настоящее время наиболее распространенным базовым механизмом обеспечения

безопасности являются цифровые сертификаты, используемые для организации

инфраструктуры открытых ключей (public key infrastructure, PKI). Наиболее

распространенным стандартом сертификата является стандарт X.509. Инфраструктура

PKI основана на использовании доверенных систем. Целью организации

инфраструктуры открытых ключей является привязка сторон обмена к сертификатам,

содержащих их открытые ключи. Это позволяет как идентифицировать пользователя по

Page 189: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 9. Обеспечение безопасности распределенных систем в .NET Framework

184

сертификату, так и использовать асимметричное шифрование с открытым и закрытым

ключами при передаче информации (рис. 9.2).

Принимающаясторона

Посылающаясторона

Шифровкаоткрытымключом

получателя

Зашифрованнаяинформация

Дешифровказакрытымключом

получателя

Открытаяинформация

Открытаяинформация

Рисунок 9.2. Шифрование с открытым ключом

Открытые ключи содержаться в сертификатах X.509, которые представляют собой

текстовый цифровой документы, связывающий набор реквизитов стороны (адрес,

название) с ее открытым ключом. Сертификат подписывается закрытым ключом

некоторой доверенной третьей стороной (certificate authority, CA). Цифровая подпись

заключается в вычислении образа сертификата, вычисленного криптографической

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

Результат шифрования образа сертификата называется цифровой подписью (рис. 9.3).

Таким образом, сертификат содержит:

– реквизиты предъявляющей его организации или подразделения организации;

– реквизиты подписавшей его доверенной третьей стороны;

– сроки действия сертификата;

– открытый ключ;

– цифровую подпись.

После получения сертификата другая сторона обмена информацией проверяет его,

используя открытый ключ подписавшей его организации. Если результат дешифровки

подписи сертификата совпадает с образом остальной части сертификата, то сертификат

верен. Сертификат доверенной стороны подписывается им самим.

Page 190: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 9. Обеспечение безопасности распределенных систем в .NET Framework

185

Проверяющая сторона

Подписывающая сторона (CA)

Вычиление образадокумента

Сравнениеполученныхобразов

Подписываемыйсертификат

Шифровка образазакрытым ключом

Объединение подписии сертификата

Сертификатс открытымключомЗакрытыйключ

Дешифровкаподписи

Вычиcление образадокумента

Рисунок 9.3. Подпись сертификата

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

выдачей сертификатов или созданный внутри организации сервер PKI (например,

Windows Certificate Services). В простейших случаях доверенная сторона может быть

представлена самоподписанным сертификатом, который выдается заранее всем

участникам обмена информацией после подписи их сертификатов соответствующим

закрытым ключом. Иногда используется и вариант с предъявлением серверу клиенту

самоподписанного сертификата. Такой сертификат не может быть проверен клиентом

на подлинность и предполагает доверие серверу, но может быть использован для

защиты передаваемой информации.

Сертификаты имеют двоякое применение:

– идентификация стороны, предъявивший сертификат;

– защита передаваемой информации парой из закрытого и открытого ключей.

При применении сертификатов основной объем информации может шифроваться и

симметрично, с использованием временного сессионного ключа. Более медленное

Page 191: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 9. Обеспечение безопасности распределенных систем в .NET Framework

186

асимметричное шифрование в таком случае применяется для передачи временного

ключа между сторонами обмена.

Сертификаты применяются во многих протоколах, из которых можно выделить

следующие применяемые с распределенными системами, построенных с .NET

Framework:

– безопасный транспортный протокол SSL;

– безопасный сетевой протокол IPsec;

– безопасная передача гипертекста HTTPS;

– расширение безопасности веб-служб WS-Security;

– MSMQ.

Как видно из данного списка, электронные сертификаты могут обеспечивать

безопасность распределенной системы как на транспортном уровне, так и на уровне

сообщений промежуточной среды.

Протокол Kerberos

Протокол Kerberos позволяет осуществлять идентификацию клиента и симметричное

шифрование трафика на основе временных сессионных ключей. В настоящее время его

модификация применятся в сетях Microsoft Windows 2000 и более поздних.

Протокол Kerberos основан на использовании третьей доверенной стороны, называемой

центром распространения ключей (key distribution center, KDC). При идентификации в

домене Active Directory клиент программной компоненты получает так называемый

билет на дарование билета (ticket-granting ticket, TGT), который затем используется для

обращения к применяющим протокол Kerberos сетевым службам. При обращении к

программной компоненте клиент посылает запрос к KDC, получает от него билет на

доступ к сервису (service ticket), содержащий реквизиты клиента для авторизации

сервисом и сессионный ключ, зашифрованные основным ключом сервиса, а так же

сессионный ключ, предназначенный для клиента. Билет на доступ к сервису

используется затем клиентом при доступе к сервису. Передаваемый сервису токен

состоит из информации из билета доступа к сервису, к которой добавляется уникальная

клиентская информация (authenticator), включающая в себя в частности время посылки

Page 192: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 9. Обеспечение безопасности распределенных систем в .NET Framework

187

запроса. Данная информация шифруется полученным клиентом сессионным ключом.

Сервис дешифрует своим основным ключом полученный от клиента билет на доступ к

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

В случае успеха дешифровки клиент проходит аутентификацию.

Протокол Kerberos используется в Microsoft Windows в:

– интегрированной безопасности Windows (Windows integrated security, WIS);

– безопасном сетевого протокола IPSec/IKE;

– расширении безопасности веб-служб WS-Security.

Протокол Kerberos в Microsoft Windows используется в пределах домена Active

Directory. WIS в частности используется службами MSMQ, IIS и COM+.

9.3. Безопасность промежуточных сред .NET Framework

Для сравнительного описания безопасности рассмотренных промежуточных сред

можно выделить следующие основные их конфигураций:

– MSMQ в пределах LAN;

– MSMQ / HTTP: запись сообщений в очереди MSMQ 3.0 через HTTP / IIS;

– Enterprise Services / COM+;

– WS: веб-службы с использованием WSE 3.0;

– Remoting без использования IIS, со стандартными каналами ;

– Remoting с использованием IIS.

В таблице 9.1 перечислены решения обеспечения безопасности указанных

конфигураций на транспортном уровне и уровне сообщений. Под VPN понимается

развертывание виртуальной частной сети на основе протоколов SSL или IPSec. Из

таблицы видно, что наибольшие трудности с точки зрения безопасности возникают с

технологией Remoting, у которой в частности нет обеспечения безопасности на уровне

промежуточной среды.

Page 193: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 9. Обеспечение безопасности распределенных систем в .NET Framework

188

Таблица 9.1. Безопасность промежуточных сред .NET Framework

Безопасность Промежуточная Среда на транспортном уровне на уровне промежуточной среды

MSMQ VPN, WIS Сертификаты X.509

MSMQ / HTTP HTTPS, VPN, WIS Нет

ES / COM+ WIS, VPN Авторизация доступа (WIS)

WS HTTPS, VPN, WIS WS-Security (X.509, Kerberos)

Remoting VPN Нет

Remoting / IIS HTTPS, VPN, WIS Нет

Применимы вместе с VPN илив полностью доверенной LAN

Применимы в домене Active Directoryв пределах LAN/VPN

Применимы в сетях общего доступа

MSMQ/HTTP Remoting / IISWS

MSMQ COM+

Remoting

Рисунок 9.4. Классификация промежуточных сред с точки зрения

безопасности распределенной системы

На рис. 9.4 представлена классификация рассмотренных сред с точки зрения

возможности применения в различных сетях различной степени доверенности.

Классификация носит вложенный характер, поскольку боле безопасные среды,

применимые в общественных сетях, могут применяться и в незащищенной локальной

сети – но не наоборот. Следует отметить, что понятие «полностью доверенной сети» не

Page 194: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 9. Обеспечение безопасности распределенных систем в .NET Framework

189

может иметь места в случае каких-либо приложений автоматизации бизнеса, а может

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

9.4. Выводы по безопасности промежуточных сред

Веб-службы с использованием WSE являются промежуточной средой с наиболее

широкими возможностями обеспечения безопасности, благодаря которым веб-службы

можно использовать в сетях общего доступа. Среды MSMQ и COM+ в основном

предназначены для создания безопасных распределенных систем в локальной сети

предприятия. Промежуточная среда Remoting может использоваться только либо

вместе со службами IIS / MSMQ, либо для организации локального взаимодействия,

либо для научных или подобных целей.

Page 195: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 10. Применение промежуточных сред

190

Тема 10. Применение промежуточных сред

Дается итоговое сравнение описанных в курсе технологий и описывается их

возможное применение в распределенных системах.

10.1. Взаимосвязь промежуточных сред

Доступные при использовании .NET Framework промежуточные среды не существуют

оторвано друг от друга. На рис. 10.1 показана взаимосвязь рассмотренных сред (с

учетом приведенного ранее примера Remoting / MSMQ). RPC на рис. 10.1 –

стандартный для Windows NT 5.* механизм удаленного вызова процедур, недоступный

для управляемого кода. Для простоты не показано возможное применение безопасных

транспортных протоколов.

MSMQMSMQMSMQRemoting

HTTP

TCP

WS

IIS

MSMQ

RPC

UDP

IP

COM+

Рисунок 10.1. Взаимосвязь промежуточных сред и транспортных протоколов

Page 196: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 10. Применение промежуточных сред

191

10.2. Сравнение технологий создания распределенных систем

В качестве некоторого итога предлагается таблица со сравнением всех описанных

ранее технологий построения распределенных систем. Среда COM+ рассматривается

при использовании модели единственного вызова объекта (JIT-activation). Среда .NET

Remoting в таблице рассмотрена в трех вариантах:

– при использовании HTTP, SOAPFormatter, IIS и режима единственного вызова;

– при использовании TCP, BinaryFormatter, вне IIS и в режиме единственного

вызова;

– при использовании TCP, BinaryFormatter, вне IIS и в режиме активации объектов

клиентом.

В графе безопасности указан вид сети, при применении в которой данная

промежуточная среда может считаться безопасной:

– WAN – сеть общего доступа;

– AD – домен Active Directory внутри LAN/VPN;

– VPN – развернутая для обеспечения безопасного обмена между компонентами

виртуальная сеть.

Таблица 10.1. Сравнение промежуточных сред

Требования

Технологии

Откры

тость

Эффективность

Масш

табируемость

Транзакции

Устойчивость

Безопасность

MSMQ + + + + AD

COM+ (JIT) – + + + + AD

Remoting/HTTP/SOAP/IIS + + + – + WAN

Remoting/TCP + + – – VPN

Remoting, актив. клиентом – – – – VPN

WS + + + + WAN

Page 197: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 10. Применение промежуточных сред

192

Пустые ячейки в таблице вызваны следующими причинами.

1. Поддержка транзакций в веб-службах ожидается в ближайшее время.

2. MSMQ сложно отнести как к закрытым (можно использовать открытые

спецификации сообщений), так и к открытым (внутренняя разработка фирмы Microsoft)

средам.

3. Это же касается и среды Remoting с бинарным форматированием: сама

спецификация является стандартизированной, но клиенту желательно иметь доступ к

сборке с объектом, и данную среду невозможно использовать вне различных

реализаций CLI.

Из таблицы 10.1 видно, что наименьшим списком достоинств обладает единственная

«родная» для .NET технология создания распределенных приложений – .NET Remoting.

Также можно сделать вывод, что клиентская активация удаленных объектов не должна

применяться в распределенных системах, отличных от экспериментальных.

Все рассмотренные промежуточные среды могут найти то или иное применение в

распределенной системе.

1. Среда .Net Remoting может быть использована для скоростного обмена данными в

доверенной сети, с возможностью безболезненного перехода на использование вместе с

IIS. Применение вместе с IIS позволяет применять технологию Remoting в любой

локальной сети и в сетях общего доступа, хотя и уступает WSE с точки зрения

безопасности и поддержки стандартов веб-служб.

2. Среда MSMQ может использоваться для обмена сообщениями между

программными компонентами в домене Active Directory и как транспорт для сред

Remoting и COM+. Использование MSMQ вместе с IIS ограничивается записью в

очередь и вряд ли найдет широкое применение.

3. Среда Enterprise Services / COM+ позволяет использовать распределенные

транзакции в локальной сети, однако она основана на технологии, разработанной для

неуправляемого кода. Можно предположить, что данная технология уже в настоящий

момент может рассматриваться как устаревшая. Тем не менее она может быть

использована при необходимости в применении распределенных транзакций и

Page 198: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 10. Применение промежуточных сред

193

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

взаимодействия на основе отложенных компонент.

4. Среда веб-служб ASP.NET / WSE является наиболее перспективной при создании

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

транзакций и возможности асинхронного взаимодействия, и в этом она уступает среде

COM+. Из всех рассмотренных технология она является наиболее предпочтительной

для организации публичных сервисов в интернете.

На рис. 10.2 приведено возможное использование рассмотренных в курсе технологий

как при создании распределенных систем внутри предприятия, так и распределенных

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

организаций может использоваться технология веб-служб (ASP.NET или Remoting), а

также MSMQ через HTTP.

Клиенты

Предприятие

Предприятие

Веб-службаASP.NET

СерверMSMQ

КлиентыКлиент WS

ПриложениеCOM+

Клиент WS

СерверRemoting

КлиентMSMQ

КлиентCOM+

КлиентыCOM+ Клиенты

Remoting

Веб-службаRemoting

Клиент WSMSMQHTTP

КлиентыКлиентMSMQ

КлиентыWS

КлиентыMSMQ

Рисунок 10.2. Возможное применение технологий построения

распределенных систем

Page 199: Поддержка разработки распределенных приложений в Microsoft .NET

Тема 10. Применение промежуточных сред

194

10.3. Выводы по применению промежуточных сред .NET Framework

Доступные при использовании .NET Framework 2.0 технологии построения

распределенных систем позволяют создавать прикладные вычислительные системы,

отвечающие предъявленным требованиям к распределенным приложениям. Основным

недостатком рассмотренных технологий является различный подход к использованию

разных промежуточных сред (например, WS и COM+), что может привезти к созданию

программного кода, который сложно переориентировать с одной технологии на

другую. Предположительно предназначенная для решения данной проблемы

технология Remoting уступает остальным рассмотренным технологиям с точки зрения

большинства требований к распределенным системам. В следующих версиях

.NET Framework будет изменен механизм доступа к промежуточным средам, что,

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

сред.

Page 200: Поддержка разработки распределенных приложений в Microsoft .NET

Список использованной литературы

195

Список использованной литературы

1. Таненбаум Э., М. ван Стеен. Распределенные системы. Принципы и

парадигмы. – СПб: Питер. – 2003 г. – 880 c.

2. Маклин С., Нафтел Дж., Уильямс К. Microsoft .NET Remoting. – Русская

Редакция, 2003 г. – 384 стр.

3. Троелсен Э. C# и платформа . NET. – CПб: Питер-пресс, 2002 г. – 800 стр.

4. Мюллер Дж. Технология COM+. – СПб: Питер, 2001 г. – 464 стр.

5. Ньюкомер Э. Веб-сервисы. XML, WSDL, SOAP и UDDI. СПб.: Питер, 2002 г. –

256 стр.

Примечание: основным источником информации по технологиям фирмы Microsoft

является библиотека Microsoft Developer Network (MSDN), доступная по адресу

http://msdn.microsoft.com.

Page 201: Поддержка разработки распределенных приложений в Microsoft .NET

Приложение I. Администрирование каталога COM+

196

Приложение I. Администрирование каталога COM+

Текущая на момент написания курса версия .NET Framework 2.0 не содержала штатных

средств администрирования каталога компонент COM+, отличных от внешней

программы регистрации и удаления компонент COM+ regsvcs.exe. В частности, в

библиотеке классов .NET Framework нет методов для подписки компонент на события

COM+. Ниже представлено одно из возможных решений этой досадной проблемы.

Первоначально необходимо создать сборку .NET с типами из библиотеки

администрирования COM и COM+, которая расположена в

%systemroot%\system32\com\comadmin.dll. Это можно сделать следующими двумя

командами.

sn /k comadmin.snk tlbimp %Systemroot%\system32\com\comadmin.dll /keyfile:comadmin.snk /out:interop.comadmin.dll /namespace:ComAdmin

Первая команда создает пару ключей для создаваемой сборки, вторая импортирует

типы из COM-библиотеки и создает подписанную сборку CLR с именем

interop.comadmin.dll, включая импортированные типы в пространство имен с именем

ComAdmin.

Для удобного использования классов созданной сборки interop.comadmin следует

реализовать некоторый промежуточный интерфейс для администрирования каталога

COM+. Данный каталог представляет собой древовидное хранилище информации о

приложениях COM+, входящих в их состав компонентах и ролях пользователей. На

рисунке I.I показана основная часть структуры каталога COM+, в скобках указаны

имена соответствующих коллекций каталога.

Page 202: Поддержка разработки распределенных приложений в Microsoft .NET

Приложение I. Администрирование каталога COM+

197

Корень каталога COM+

ПриложенияCOM+

(Applications)

Временныеподписки

СерверыкластераCOM+

ПротоколыDCOM

Список известныхкомпьютеров c

COM+

Компоненты приложения (Components) Роли пользователей (Roles)

Интерфейсы компоненты(InterfacesForComponents)

Роли компоненты(RolesForComponent)

Подписки компоненты(SubscriptionsForComponent)

Методы интерфейса(MethodsForInterface)

Роли интерфейса(RolesForInterface)

Роли метода(RolesForMethod)

Рисунок I.I. Упрощенная структура каталога COM+

Приведенные в курсе примеры используют следующий вспомогательный класс LceUtils

для создания постоянной подписки. Далее идет полное описание этого класса.

// Файл SevaComLCE.cs using System; using ComAdmin; namespace Seva.ComUtils { public static class LceUtils { // Создание постоянной подписки public static void PermanentSubscription(string appName, string subscriberName, string eventGuid, string interfaceGuid) { // Создание объекта для доступа к каталогу COM+ COMAdminCatalog catalog = new ComAdmin.COMAdminCatalogClass(); ICatalogObject CatalogObject = null; ICatalogCollection CompCollection = null, SubCollection = null; // Получение списка приложений COM+ ICatalogCollection AppCollection = (ICatalogCollection) catalog.GetCollection("Applications"); AppCollection.Populate();

Page 203: Поддержка разработки распределенных приложений в Microsoft .NET

Приложение I. Администрирование каталога COM+

198

// Поиск нужного приложения по его имени for (int Counter = 0; Counter < AppCollection.Count; Counter++) { CatalogObject = (ICatalogObject) AppCollection.get_Item(Counter); if ((String)CatalogObject.Name == appName) { // Получение компонент приложения CompCollection = (ICatalogCollection) AppCollection.GetCollection("Components", CatalogObject.Key); CompCollection.Populate(); break; } } if (CompCollection == null) { throw new ApplicationException( String.Format("COM+ Application not found : [{0}]", appName)); } // Поиск указанной компоненты for (int Counter = 0; Counter < CompCollection.Count; Counter++) { CatalogObject = (ICatalogObject) CompCollection.get_Item(Counter); if ((String)CatalogObject.Name == subscriberName) { // Получение списка подписок компоненты SubCollection = (ICatalogCollection) CompCollection.GetCollection( "SubscriptionsForComponent", CatalogObject.Key); SubCollection.Populate(); break; } } if (SubCollection == null) { throw new ApplicationException( String.Format("Component not found : [{0}]", subscriberName)); } // Создание подписки CatalogObject = (ICatalogObject)SubCollection.Add(); CatalogObject.set_Value("EventCLSID", "{" + eventGuid + "}"); CatalogObject.set_Value("Name", "Permanent Subscription"); CatalogObject.set_Value("InterfaceID", "{" + interfaceGuid + "}"); CatalogObject.set_Value("Enabled", true); // Сохранение изменений SubCollection.SaveChanges(); } // Subscribe } //LceUtils } // Seva.ComUtils

Page 204: Поддержка разработки распределенных приложений в Microsoft .NET

Приложение II. Использование ASP.NET без IIS

199

Приложение II. Использование ASP.NET без IIS

В учебном процессе или при тестировании приложений иногда возникает потребность

работы с веб-службами ASP.NET без использования IIS. При использовании .NET

Framework 2.0 и операционной системы Windows XP SP2 или Windows Server 2003

можно достаточно просто создать свой носитель веб-служб на основе классов

HttpListener и HttpRuntime, при этом служба IIS может быть не установлена в системе.

Далее приведен пример простого класса, позволяющего осуществлять размещение

приложений ASP.NET в учебных целях.

// SevaAspHost.cs using System; using System.IO; using System.NET; using System.Web; using System.Web.Hosting; using System.Text.RegularExpressions; namespace Seva.AspHost { public class AspHost: MarshalByRefObject { private HttpListener listener; private string virtualDir; private string hostingDir; public string VirtualDir { get {return virtualDir;} } public string HostingDir { get {return hostingDir;} } public static AspHost CreateHost(string[] prefixes, string aspVirtualDir, string aspHostingDir) { if (!HttpListener.IsSupported) { throw new NotSupportedException( "Требуется Windows XP SP2/Server 2003."); } AspHost host = (AspHost) ApplicationHost.CreateApplicationHost( typeof(AspHost), aspVirtualDir, aspHostingDir); host.Init(prefixes, aspVirtualDir, aspHostingDir); return host; }

Page 205: Поддержка разработки распределенных приложений в Microsoft .NET

Приложение II. Использование ASP.NET без IIS

200

public void Init(string[] prefixes, string aspVirtualDir, string aspHostingDir) { virtualDir = aspVirtualDir; hostingDir = aspHostingDir; listener = new HttpListener(); foreach (string prefix in prefixes) listener.Prefixes.Add(prefix); } public void Start() { listener.Start(); } public void Stop() { listener.Stop(); } public void ProcessRequest() { HttpListenerContext context = listener.GetContext(); HttpListenerWorkerRequest workerRequest = new HttpListenerWorkerRequest(context, this); HttpRuntime.ProcessRequest(workerRequest); } } public class HttpListenerWorkerRequest: HttpWorkerRequest { private HttpListenerContext context; private AspHost host; public HttpListenerWorkerRequest(HttpListenerContext listenerContext, AspHost aspHost) { context = listenerContext; host = aspHost; } public override void EndOfRequest() { context.Response.OutputStream.Close(); context.Response.Close(); } public override void FlushResponse(bool finalFlush) { context.Response.OutputStream.Flush(); } public override string GetHttpVerbName() { return context.Request.HttpMethod; }

Page 206: Поддержка разработки распределенных приложений в Microsoft .NET

Приложение II. Использование ASP.NET без IIS

201

public override string GetHttpVersion() { return string.Format("HTTP/{0}", context.Request.ProtocolVersion.ToString()); } public override string GetLocalAddress() { return context.Request.LocalEndPoint.Address.ToString(); } public override int GetLocalPort() { return context.Request.LocalEndPoint.Port; } public override string GetQueryString() { return context.Request.Url.Query.TrimStart(new char[]{'?'}); } public override string GetRawUrl() { return context.Request.RawUrl; } public override string GetRemoteAddress() { return context.Request.RemoteEndPoint.Address.ToString(); } public override int GetRemotePort() { return context.Request.RemoteEndPoint.Port; } public override string GetUriPath() { return context.Request.Url.LocalPath; } public override void SendKnownResponseHeader(int index, string value) { context.Response.Headers[GetKnownResponseHeaderName(index)] = value; } public override void SendResponseFromMemory(byte[] data, int length) { context.Response.OutputStream.Write(data, 0, length); } public override void SendStatus(int statusCode, string statusDescription) { context.Response.StatusCode = statusCode; context.Response.StatusDescription = statusDescription; } public override void SendUnknownResponseHeader(string name, string value) { context.Response.Headers[name] = value; }

Page 207: Поддержка разработки распределенных приложений в Microsoft .NET

Приложение II. Использование ASP.NET без IIS

202

public override void SendResponseFromFile(IntPtr handle, long offset, long length) { } public override void SendResponseFromFile(string filename, long offset, long length) { } public override string GetAppPath() { return host.VirtualDir; } public override string GetAppPathTranslated() { return host.HostingDir; } public override string GetUnknownRequestHeader(string name) { return context.Request.Headers[name]; } public override string GetKnownRequestHeader(int index) { switch (index) { case HeaderUserAgent: return context.Request.UserAgent; default: return context.Request.Headers[GetKnownRequestHeaderName(index)]; } } public override string GetFilePath() { string s = context.Request.Url.LocalPath; Regex re = new Regex(@"^(.*\.as\wx)\/\w+$"); Match m = re.Match(s); if (m.Success) s = m.Groups[1].Value; return s; } public override string GetFilePathTranslated() { string s = GetFilePath().Substring(host.VirtualDir.Length); return host.HostingDir + s.Replace('/', '\\'); } public override string GetPathInfo() { return context.Request.Url.LocalPath.Substring(GetFilePath().Length); } public override int ReadEntityBody(byte[] buffer, int size) { return context.Request.InputStream.Read(buffer, 0, size); } } }

Page 208: Поддержка разработки распределенных приложений в Microsoft .NET

Приложение II. Использование ASP.NET без IIS

203

Далее приведен пример использования этого класса, позволяющий хранить

ASP-страницы в поддиректории www и обращаться к ним по локальным адресам и

порту 8080.

// AspServer.cs using System; using System.IO; using Seva.AspHost; class Program { static void Main(string[] args) { string[] prefixes = new string[] { "http://localhost:8080/", "http://127.0.0.1:8080/" }; AspHost host = AspHost.CreateHost(prefixes, "/", Directory.GetCurrentDirectory()+@"\www"); host.Start(); foreach (string s in prefixes) Console.WriteLine(s); while (true) host.ProcessRequest(); } }

Используемый при вызове метода ApplicationHost.CreateApplicationHost тип (в данном

случае – AspHost) должен находиться, как один из вариантов, в глобальной сборке.

Далее приводится make-файл для создания простейшего носителя ASP.NET.

all: ASPServer.exe ASPServer.exe: *.cs AspHost.dll csc /out:ASPServer.exe AspServer*.cs /r:AspHost.dll AspHost.dll: SevaAspHost.cs AspHost.key csc /out:ASPHost.dll /t:library SevaAspHost.cs /keyfile:AspHost.key AspHost.key: sn -k AspHost.key install: gacutil -i ASPHost.dll if not exist www md www

После команды nmake && nmake install в поддиректорию www можно помещать

страницы ASP.NET с расширениями aspx и asmx.

Page 209: Поддержка разработки распределенных приложений в Microsoft .NET

Приложение III. Симметричное шифрование

204

Приложение III. Симметричное шифрование

В теме, посвященной среде .NET Remoting, для шифрования передаваемых по каналу

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

предоставляет интерфейс к стандартному классу FCL RijndaelManaged, реализующему

алгоритм шифрования Рижндала. Используется версия алгоритма с генерацией

случайного вектора инициализации, который передается вместе с зашифрованными

данными. Основные методы класса – Encrypt и Decrypt реализуют соответственно

шифровку и дешифровку содержимого входного потока и запись результата в

выходной поток.

// Файл SevaSecurityCipher.cs using System; using System.IO; using System.Security.Cryptography; namespace Seva.Security.Encryption { public class SymmetricEncryptor { public const int KeySize = 32; public const int BlockSize = 32; RijndaelManaged cipher = null; public byte[] Key { get { return cipher.Key; } set { cipher.Key = value; } } public SymmetricEncryptor() { cipher = CreateCipher(); cipher.GenerateKey(); } public SymmetricEncryptor(byte[] key) { cipher = CreateCipher(); cipher.Key = key; }

Page 210: Поддержка разработки распределенных приложений в Microsoft .NET

Приложение III. Симметричное шифрование

205

public void SaveKey(string fileName) { using (FileStream file = new FileStream(fileName, FileMode.Create, FileAccess.Write)) { file.Write(Key, 0, Key.Length); } } public void LoadKey(string fileName) { byte[] buffer = new byte[KeySize]; using (FileStream file = new FileStream(fileName, FileMode.Open, FileAccess.Read)) { file.Read(buffer, 0, KeySize); } Key = buffer; } public byte[] CreateEmptyIv() { return new byte[SymmetricEncryptor.BlockSize]; } public byte[] EncryptMessage(byte[] plainText, out byte[] iv) { cipher.GenerateIV(); iv = cipher.IV; ICryptoTransform transform = cipher.CreateEncryptor(); byte[] cipherText = transform.TransformFinalBlock(plainText, 0, plainText.Length); return cipherText; } public byte[] DecryptMessage(byte[] cipherText, byte[] iv) { cipher.IV = iv; ICryptoTransform transform = cipher.CreateDecryptor(); byte[] plainText = transform.TransformFinalBlock(cipherText, 0, cipherText.Length); return plainText; } public void Encrypt(Stream input, Stream output) { byte[] iv = null; byte[] buffer = new byte[input.Length]; input.Read(buffer, 0, buffer.Length); buffer = EncryptMessage(buffer, out iv); // output.Write(iv, 0, iv.Length); output.Write(buffer, 0, buffer.Length); }

Page 211: Поддержка разработки распределенных приложений в Microsoft .NET

Приложение III. Симметричное шифрование

206

public void Decrypt(Stream input, Stream output) { byte[] iv = CreateEmptyIv(); byte[] buffer = new byte[input.Length-iv.Length]; input.Read(iv, 0, iv.Length); input.Read(buffer, 0, buffer.Length); // buffer = DecryptMessage(buffer, iv); output.Write(buffer, 0, buffer.Length); } private RijndaelManaged CreateCipher() { RijndaelManaged cipher = new RijndaelManaged(); cipher.KeySize = KeySize * 8; cipher.BlockSize = BlockSize * 8; cipher.Mode = CipherMode.CBC; cipher.Padding = PaddingMode.PKCS7; return cipher; } } } // SevaSecurityCipher.cs

Для создания секретного ключа можно использовать следующую программу,

записывающую созданный ключ в файл.

// Файл makekey.cs using System; using System.IO; using Seva.Security.Encryption; class MainApp { public static void Main(string[] Args) { if (Args.Length != 1) { Console.WriteLine("usage: makekey <key_file>"); return; } SymmetricEncryptor encryptor = new SymmetricEncryptor(); encryptor.SaveKey(Args[0]); } } // Файл makekey.cs

Ниже приведена строка компиляции для программы создания ключей.

csc MakeKey.cs SevaSecurityCipher.cs