Интернет-журнал "Домашняя лаборатория", 2007 №6
Шрифт:
И наконец необходимо упомянуть о передаче объектов по значению. Это возможно и полезно если объект небольшой. В этом случае клиент получает не прокси на объект, а его копию. Для передачи объекта по значению необходимо, чтобы соответствующий класс был определен с пользовательским атрибутом [Serializable] для использования стандартного метода сериализации, либо можно самостоятельно реализовать интерфейс ISeriaiizabie для задания собственного способа сериализации. Пользовательские атрибуты и вопросы их использования
Обработка ошибок
До сих пор мы не рассматривали обработку ошибок. В СОМ каждый метод каждого интерфейса (за исключением методов AddRef и Release интерфейса IUnknown) должен возвращать значение типа HRESULT, говорящее об успехе или неудаче вызова метода и о причине неудачи.
Получатель этого значения должен его обработать. Но у него не всегда есть возможность сделать это.
В.NET используется технология обработки исключений — блоки try, catch, finally, инициализация исключения — throw.
В нашем распределенном примере при запуске клиента без запуска сервера возникает никем не перехваченная ошибка. Включим часть кода клиента, где происходит работа с сервером, в блок try и добавим блоки catch и finally для задания реакции на ошибки и на выход из блока try.
using System;
using MyServer;
using System. Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Net;
public class MyApp {
public static void Main {
HttpChannel с = new HttpChannel;
ChannelServices.RegisterChannel(c);
try {
Account a = (Account)Activator.GetObject(typeof(Account),
"http://localhost:8080/Account",
WellKnownObjectMode.Singleton);
a. Add(5);
Console.WriteLine("Total = {0}", a.Totalf));
}
catch(WebException e) {
Console.WriteLine(e.Message);
}
catch(Exception e){
Console.WriteLine(e.Message);
}
finally {
Console.WriteLine("Bye");
}
}
}
Первый блок catch перехватывает специальное исключение WebException, связанное именно с работой http канала, а второй блок catch перехватывает все остальные исключения. Независимо от наличия ошибки и ее типа всегда отрабатывает блок finally.
Ниже приведены примеры сообщений, получаемых в консольном окне при запуске клиента до и после запуска сервера.
Клиент запущен до запуска сервера
>МуАрр. ехе
The underlying connection was closed: Unable to connect
to the remote server
Bye
>
Клиент
>МуАрр. ехе
Total = 5
Bye
>
Необходимо еще одно дополнительное замечание. Если имеет место цепочка вызовов, то необработанное исключение поднимается вверх по цепочке вызовов до блока catch, способного его обработать. Это позволяет сделать обработку в наиболее удобном месте. Возможна и частичная обработка ошибки на каждом уровне при ее передаче вверх по стеку вызовов.
Синхронизация
Теперь рассмотрим случай двух клиентов, параллельно посылающих некоторые суммы на один и тот же счет, поддерживаемый нашим сервером.
Вспомним, что в СОМ использовались апартаменты типа STA для объектов, не допускающих параллельный вызов своих методов, и типа МTА для потоко-безопасных объектов. В.NET по умолчанию считается, что все объекты являются потоко-безопасными.
Проверим это, испортив наш сервер для большей наглядности. В локальной переменной метода Add сохраняется текущая величина счета, после чего текущий поток засыпает на 1 миллисекунду и, проснувшись, делает текущее значение счета равным сумме значения, сохраненного в локальной переменной, и полученной от клиента величине нового вклада.
Сервер
……
using System.Threading;
……
public class Account: MarshalByRefObject, IAccumulator, IAudit {
……
public void Add(int sum) {
int s = _sum;
Thread.Sleep(1);
_sum = s + sum;
}
…..
Клиент посылает на сервер 1000 раз по 5 условных единиц и после этого выводит на свою консоль общую отправленную сумму.
Клиент
…..
int sentTotal = 0;
for (int i = 0; i < 1000; i++) {
a. Add(5);
sentTotal +=5;
}
Console.WriteLine("Sent totally by this client = {0}",
sentTotal);
…..
Запустим сервер и затем с небольшим временным интервалом двух клиентов. Каждый из запущенных клиентов по завершении своей работы выведет на свою консоль следующие строки:
>МуАрр. ехе
Sent totally by this client = 5000 Bye
>
Несложно написать клиентскую программу, которая обращается к работающему серверу и выводит на свою консоль текущую величину счета. Здесь для краткости эта программа (total.ехе) не приведена, но для примера приводится результат ее работы (программа total была запущена после завершения работы обоих клиентов)