Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции OOP c#.doc
Скачиваний:
44
Добавлен:
22.09.2019
Размер:
3.38 Mб
Скачать

3.5. Удаленные Объекты с клиентской активацией

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

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

Начнем решение задачи с разработки класса для представления удаленного объекта. Первая «заготовка» класса может выглядеть следующим образом (сборка RemoteCAO.dll):

using System;

namespace RemoteCAO {

public class UserCalculator: MarshalByRefObject {

private bool Registered = false;

public double CachedValue;

public bool Register(string Name, string Pass) {

Registered = (Name == "user") && (Pass == "pass");

if (Registered)

Console.WriteLine("User {0} registered", Name);

else

Console.WriteLine("Access denied");

return Registered;

}

public double Add(double x, double y) {

if (Registered) {

CachedValue = x + y;

return CachedValue;

}

else throw new Exception("Not registered!");

}

}

}

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

Класс UserCalculator, как и любой удаленный тип, является наследником класса MarshalByRefObject. В таблице 19 приведено описание методов класса MarshalByRefObject.

Таблица 19

Методы класса MarshalByRefObject

Имя метода

Описание

CreateObjRef()

Виртуальный метод, возвращающий объект класса System.Runtime.Remoting.ObjRef. Такой объект необходим клиентскому приложению, чтобы настроить прокси-объект. Метод можно переписать, если требуется собственная реализация ObjRef.

GetLifetimeService()

Функция возвращает объект, реализующий интерфейс ILease, то есть лицензию, связанную с объектом.

InitializeLifetimeService()

Данный виртуальный метод вызывается инфраструктурой Remoting при создании объекта и должен возвращать лицензию объекта. При необходимости метод можно переписать.

В нашем примере мы собираемся использовать нестандартные времена для лицензии объектов класса UserCalculator. Следовательно, нам требуется переписать виртуальный метод InitializeLifetimeService():

namespace RemoteCAO {

public class UserCalculator: MarshalByRefObject {

. . .

public override object InitializeLifetimeService() {

ILease lease = (ILease)base.InitializeLifetimeService();

if (lease.CurrentState == LeaseState.Initial) {

lease.InitialLeaseTime = TimeSpan.FromMinutes(4);

lease.SponsorshipTimeout = TimeSpan.FromMinutes(1);

lease.RenewOnCallTime = TimeSpan.FromMinutes(3);

}

return lease;

}

}

}

В начале работы метода InitializeLifetimeService() вызывается метод базового класса, который возвращает стандартную лицензию. Параметры можно устанавливать у лицензии, которая не была помещена в диспетчер лицензий. В методе выполняется такая проверка (проверяется текущее состояние лицензии). В случае успеха устанавливаются требуемые значения свойств лицензии. Если метод InitializeLifetimeService() переписан так, что возвращает значение null, то лицензия объекта никогда не заканчивается.

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

using System;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Http;

using RemoteCAO;

class CalcServer {

public static void Main() {

HttpChannel chan = new HttpChannel(6000);

ChannelServices.RegisterChannel(chan);

RemotingConfiguration.ApplicationName = "MyServer";

RemotingConfiguration.RegisterActivatedServiceType(

typeof(UserCalculator));

Console.WriteLine("Press [enter] to exit...");

Console.ReadLine();

}

}

В сервере регистрируется канал и удаленный тип с клиентской активацией. Задается имя приложения ("MyServer"). Оно будет являться частью URL при доступе к удаленному объекту1.

Приступим к написанию приложения-клиента:

using System;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Http;

using RemoteCAO;

class CalcClient {

public static void Main() {

HttpChannel chan = new HttpChannel(0);

ChannelServices.RegisterChannel(chan);

RemotingConfiguration.RegisterActivatedClientType(

typeof(RemoteCAO.UserCalculator),

"http://localhost:60000/MyServer");

UserCalculator calc = new UserCalculator();

Console.Write("Input name: ");

string name = Console.ReadLine();

Console.Write("Input password: ");

string pass = Console.ReadLine();

if (calc.Register(name, pass))

Console.WriteLine("Successful logon");

else Console.WriteLine("Failed....");

try {

double res = calc.Add(2, 3);

Console.WriteLine(res);

}

catch(Exception e) {

Console.WriteLine(e.Message);

}

Console.WriteLine("Cached value {0}",

calc.CachedValue);

Console.ReadLine();

}

}

В клиенте требуется обратить внимание на следующие особенности. При создании канала конструктору передан параметр 0, так как планируется принимать обратные вызовы от сервера. При регистрации типа указано (как часть URL) имя приложения-клиента. После того как на клиенте зарегистрирован удаленный тип с клиентской активацией, для создания удаленных объектов используется оператор new. В клиенте мы можем обрабатывать исключительные ситуации, которые возникли на сервере и работать с полем удаленного объекта.

Реализуем спонсора, который сможет продлить время жизни удаленного объекта. Будем использовать клиентский спонсор, то есть объект-спонсор разместим на клиенте. Напомним, что класс для спонсоров должен реализовывать интерфейс ISponsor, а также быть MBR-типом:

using System.Runtime.Remoting.Lifetime;

. . .

public class FatSponsor: MarshalByRefObject, ISponsor {

public TimeSpan Renewal(ILease lease) {

Console.WriteLine("Renewal called....");

return TimeSpan.FromMinutes(1);

}

}

class CalcClient {. . .}

Наш спонсор прост. Он реализует метод ISponsor.Renewal(), который возвращает (независимо от лицензии) время, на которое продлевается лицензия объекта (1 минута).

Объект-спонсор требуется создать и зарегистрировать. Для этого у удаленного объекта запрашивается лицензия (методом GetLifetimeService()), а затем при помощи метода ILease.Register() регистрируется спонсор:

// Код в теле метода Main()

// Создаем спонсора

ISponsor s = new FatSponsor();

// Запрашиваем у удаленного объекта лицензию

ILease currLease = (ILease)calc.GetLifetimeService();

Console.WriteLine(currLease.SponsorshipTimeout);

// Регистрируем спонсора

currLease.Register(s);

Метод ILease.Unregister() используется для отмены регистрации определенного спонсора.

Приведенный код имеет следующую особенность. Он работает только с Microsoft Framework 1.0. В версии 1.1 и выше данный код работать не будет. В целях безопасности в этих версиях форматерам запрещено передавать вызовы от сервера клиенту без дополнительных настроек. Если проводится настройка Remoting с помощью конфигурационных файлов, то в параметрах форматера (и на клиенте, и на сервере) требуется поместить такой текст (выделен жирным шрифтом):

<channels>

<channel ref="http" port="0" >

<serverProviders>

<formatter ref="soap" typeFilterLevel="Full" />

<formatter ref="binary" typeFilterLevel="Full" />

</serverProviders>

</channel>

</channels>