Шрифт:
Наш удаленный объект должен жить в некотором приложении. Здесь мы описываем консольное приложение, функция 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: MyServer\MyServer.ехе МуАрр. cs
>MyApp.ехе
Total = 5
Client AppDomain = MyApp.exe
>MyApp.ехе
Total = 10
Client AppDomain = MyApp.exe
>
Несколько комментариев, касающихся удаленных компонентов.
В.NET определены три типа режимов работы удаленных объектов:
1. SingleCall
Удаленный объект активируется на стороне сервера только при получении вызова какого-либо его метода от клиента и по выполнении этого метода сразу же уничтожается.
2. Singleton
Удаленный объект совместно используется несколькими клиентами, сохраняя состояние между вызовами. Для контроля за его жизненным циклом используется распределенная сборка мусора — лизинг. При этом объект получает некоторое время жизни, продлеваемое автоматически после каждого нового вызова. После исчерпания этого времени следует запрос к спонсору объекта (обычно к самому клиенту) о продлении жизни компонента. При отсутствии ответа объект уничтожается. Такой механизм позволяет экономить на коммуникации между клиентом и удаленным сервером.
3. Активируемый клиентом
Такой объект доступен только одному клиенту и сохраняет состояние между вызовами. Для контроля за жизненным циклом объекта используется лизинг.
Говоря об удаленных компонентах и сопоставляя .NET с СОМ нельзя не вспомнить об апартаментах. В одном процессе может быть несколько апартаментов, и ссылка на объект, полученная в одном апартаменте, не может непосредственно использоваться в другом апартаменте. Необходимо выполнить ее маршалинг между апартаментами. В.NET такой проблемы нет. Любая объектная ссылка (прокси на удаленный компонент) используется глобально по всему домену приложения.
И наконец необходимо упомянуть о передаче объектов по значению. Это возможно и полезно если объект небольшой. В этом случае клиент получает не прокси на объект, а его копию. Для передачи объекта по значению необходимо, чтобы соответствующий класс был определен с пользовательским атрибутом [Serializable] для использования стандартного метода сериализации, либо можно самостоятельно реализовать интерфейс ISeriaiizabie для задания собственного способа сериализации. Пользовательские атрибуты и вопросы их использования будут рассмотрены далее.
Обработка ошибок
До сих пор мы не рассматривали обработку ошибок. В СОМ каждый метод каждого интерфейса (за исключением методов AddRef и Release интерфейса IUnknown) должен возвращать значение типа HRESULT, говорящее об успехе или неудаче вызова метода и о причине неудачи.
Получатель этого значения должен его обработать. Но у него не всегда есть возможность сделать это.