Добавить в цитаты Настройки чтения

Страница 211 из 372

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

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

Удаленный сервер

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

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

Сервер

//MyServer.cs

using System;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Cha

using System.Runtime.Remoting.Cha

namespace MyServer {

public interface IAccumulator {

       void Add(int sum);

}

public interface IAudit {

       int Total();

}

public class Account: MarshalByRefObject, IAccumulator, IAudit {

        protected int _sum = 0;

        public void Add(int sum) {

               _sum += sum;

        }

        public int Total () {

                Console.WriteLine("Server AppDomain = {0}",

                       AppDomain.CurrentDomain.FriendlyName);

                return _sum;

         }

}

public class AccountApp {

         public static void Main() {

                 HttpCha

                 Cha

                 RemotingConfiguration.RegisterWellKnownServiceType (

                        typeof(Account),

                        "Account",

                        WellKnownObjectMode.Singleton);

                Console.WriteLine("Server is listening");

                Console.ReadLine();

                Console.WriteLine("Bye");

             }

}

}

Клиент

//МуАрр. сs

using System; using MyServer;





using System.Runtime.Remoting;

using System.Runtime.Remoting.Cha

using System.Runtime.Remoting.Cha

public class MyApp {

          public static void Main() {

                HttpCha

                Cha

                Account a = (Account)Activator.GetObject (

                      typeof(Account), "http://localhost:8080/Account",

                      WellKnownObjectMode.Singleton);

                a. Add(5);

                Console.WriteLine("Total = {0}", a.Total());

                Console.WriteLine("Client AppDomain = {0}",

                     AppDomain.CurrentDomain.FriendlyName);

        }

}

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

System.Runtime.Remoting,

System.Runtime.Remoting.Cha

System.Runtime.Remoting.Cha

Последнее пространство имен необходимо для работы с каналом, использующим http. Есть возможность работы с tcp каналом (что в несколько раз быстрее), но http канал по умолчанию передает сообщения по протоколу SOAP в стандартизованном XML формате, a tcp по умолчанию передает SOAP сообщения в нестандартизованном бинарном формате. Иными словами, при использовании html канала потенциальный клиент может вообще не принадлежать миру .NET и платформе Windows.

Необходимо обратить внимание на то, что класс Account теперь наследует классу MarshalByRefObject. Все объекты в .NET делятся на три типа:

1. передаваемые по ссылке

2. передаваемые по значению

3. не передаваемые за пределы своего домена приложения

Наш класс должен быть передаваем по ссылке. Это обеспечивается тем, что он наследует классу MarshalByRefObject. При работе с таким объектом клиент реально работает не с самим объектом, а с его прокси. Сам же объект формируется удаленно.

Наш удаленный объект должен жить в некотором приложении. Здесь мы описываем консольное приложение, функция Main (в коде сервера) — его точка входа.

В данном приложении формируется объект — http-канал (с указанием произвольного номера порта) и этот канал регистрируется в системе Remoting.

После этого в этой же системе регистрируется класс, к которому будет обеспечен доступ удаленных клиентов. Задается его тип, его URI — определяемый разработчиком идентификатор, и режим работы объекта. В данном случае это Singleton. Это означает, что такой объект создается после получения первого запроса от какого-либо клиента и далее живет (сохраняя состояние), отвечая на запросы как этого, так и других объектов.

После запуска серверного приложения на консоль сервера выдается уведомление о том, что сервер готов к работе. С этого момента сервер ожидает получения сообщений по каналу http на порт 8080. Остановить работу сервера можно нажав на клавишу <Enter>.

Теперь обратимся к клиенту. Он регистрирует один из каналов, зарегистрированных сервером (но не указывает номер порта).

Метод Activator.Getobject возвращает ссылку на прокси, сам же объект при этом не создается. Если он не был создан ранее, то он будет создан при получении первого запроса от какого-либо клиента. Среди параметров задаются тип компонента, путь к нему и режим его работы. Все остальное не изменилось по сравнению с клиентом, предназначенным для работы с сервером в домене клиентского приложения.

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

Консоль сервера

>csc /t: exe MyServer.cs

>MyServer.ехе

Server is listening

Server AppDomain = MyServer.exe

Server AppDomain = MyServer.exe

Bye

>

Консоль клиента

>csc /r: MyServerMyServer.ехе МуАрр. cs

>MyApp.ехе

Total = 5

Client AppDomain = MyApp.exe

>MyApp.ехе