C# для профессионалов. Том II - Симон Робинсон
Шрифт:
Интервал:
Закладка:
protected static void ShowChannelProperties(IChannelReceiver channel) {
Console.WriteLine("Name; " + channel.ChannelName");
Console.WriteLine("Priority: " + channel.ChannelPriority);
if (channel is HttpChannel) {
HttpChannel httpChannel = channel as HttpChannel;
Console.WriteLine("Scheme: " + httpChannel.ChannelScheme);
}
ChannelDataStore data = (ChannelDataStore)channel.ChannelData;
foreach (string uri in data.ChannelUris) {
Console.WriteLine("URI: " + uri);
}
Console.WriteLine();
}
После создания каналов вызывается метод ShowChannelProperties():
TcpServerChannel tcpChannel = new TcpServerChannel(8086);
ShowChannelProperties(tcpChannel);
HttpServerChannel httpChannel = new HttpServerChannel(8085);
ShowChannelProperties(httpChannel);
С помощью каналов TCP и HTTP будет получена следующая информация:
Как можно видеть, именем по умолчанию для TcpServerChannel будет tcp, а канал HTTP называется http. Оба канала имеют свойство по умолчанию, равное 1 (в конструкторах заданы порты 8085 и 8086). URI каналов показывает протокол, имя хоста (в данном случае CNagel) и номер порта.
Задание свойств канала
Можно задать все свойства канала в списке с помощью конструктора TcpServerChannel(IDictionary, IServerChannelSinkProvider). Класс ListDictionary реализует IDictionary, поэтому свойства Name, Priority и Port задаются с помощью этого класса.
Для использования класса ListDictionary необходимо объявить использование пространства имен System.Collections.Specialized. В дополнение к параметру IDictionary передается параметр IServerChannelSinkProvider, в данном случае SoapServerFormatterSinkProvider вместо BinaryServerFormatterSinkProvider, который используется по умолчанию для TCPServerChannel. Реализация по умолчанию класса SoapServerFormatterSinkProvider ассоциирует класс SoapServerFormatterSink с каналом, применяющим SoapFormatter для преобразования данных передачи:
ListDictionary properties = new ListDictionary();
properties.Add("Name", "TCP Channel with a SOAP Formatter");
properties.Add("Priority", "20");
properties.Add("Port", "8086");
SoapServerFormatterSinkProvider sinkProvider =
new SoapServerFormatterSinkProvider();
TcpServerChannel tcpChannel =
new TcpServerChannel(properties.sinkProvider);
ShowChannelProperties(tcpChannel);
Вывод, который будет получен из запускаемого на сервере кода, показывает новые свойства канала TCP:
Подключаемость канала
Можно создать специальный канал для отправки сообщений с помощью транспортного протокола, отличного от HTTP или TCP, или расширить существующие каналы:
□ Посылающая часть должна реализовать интерфейс IChannelSender. Наиболее важным является метод CreateMessageSink(), где клиент посылает URL, с помощью него создается экземпляр соединения с сервером. Здесь должен быть создан приемник сообщений, затем он используется прокси для отправки сообщений в канал.
□ Получающая часть должна реализовать интерфейс IChannelReceiver. Необходимо запустить прослушивание с помощью свойства ChannelData, затем ожидать отдельного потока выполнения для получения данных от клиента. После демаршализации сообщения метод ChannelServices.SyncDispatchMessage() может использоваться для отправки сообщения объекту.
Форматтеры
.NET Framework предоставляет два класса форматтера:
□ System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.
□ System.Runtime.Serialization.Formatters.Soap.SoapFormatter.
Форматтер ассоциируется с каналом из-за наличия объектов приемников форматтера и провайдеров источников форматтера.
Оба эти класса форматтера реализуют интерфейс System.Runtime.Remoting.Messaging.IRemotingFormatter, который определяет методы Serialize() и Deserialize() для передачи и приема данных из канала.
Форматтер является подключаемым. При создании класса собственного форматтера экземпляр должен ассоциироваться с используемым каналом. Это делается с помощью приемника форматтера и провайдера приемника форматтера. Как было показано ранее, провайдер источника форматтера, например SoapServerFormatterSinkProvider, может передаваться как аргумент при создании канала. Провайдер источника форматтера реализует для сервера интерфейс IServerChannelSinkProvider, а для клиента IClientChannelSinkProvider. Оба эти интерфейса определяют метод CreateSink(), возвращающий источник форматтера, — SoapServerFormatterSinkProvider даст экземпляр класса SoapServerFormatterSink. На клиентской стороне имеется класс SoapClientFormatterSink, который использует в методах SyncProcessMessage() и AsyncProcessMessage() класс SoapFormatter для сериализации сообщения. SoapServerFormatterSink десериализует сообщение снова с помощью SoapFormatter.
Все эти классы приемника и провайдера способны расширяться и заменяться собственными реализациями.
ChannelServices и RemotingContiguration
Служебный класс ChannelServices используется для регистрации каналов в среде выполнения .NET Remoting. С помощью этого класса можно также получить доступ ко всем зарегистрированным каналам. Это крайне полезно, если для конфигурирования канала используются конфигурационные файлы, так как в этом случае канал создается неявно (см. ниже).
Канал регистрируется с помощью статического метода ChannelServices.RegisterChannel().
Здесь представлен серверный код для регистрации каналов HTTP и TCP:
TcpChannel tcpChannel = new TcpChannel(8086);
HttpChannel httpChannel = new HttpChannel(8085);
Channel Services.RegisterChannel(tcpChannel);
СhannelServices.RegisterChannel(httpChannel);
Служебный класс ChannelServices можно теперь использовать для отправки синхронных и асинхронных сообщений и для отмены регистрации определенных каналов. Свойство RegisteredChannels возвращает массив IChannel всех зарегистрированных каналов. Возможно также и< пользование метода GetChannel() для доступа к определенному каналу по его имени. С помощью ChannelServices пишется специальная административная утилита для управления каналами. Вот небольшой пример показывающий как можно остановить режим прослушивания канала:
HttpServerChannel channel =
(HttpServerChannel)ChannelServices.GetChannel("http");
channel.StorListening(null);
Класс RemotingConfiguration — другой служебный класс .NET Remoting. На серверной стороне он используется для регистрации типов удаленных объектов активированных на сервере объектов и для маршализации удаленных объектов в ссылочный класс маршализованного объекта ObjRef. ObjRef является сериализуемым представлением объекта, которое было послано по линии связи. На клиентской стороне работает RemotingServices, демаршализуя удаленный объект, чтобы создать прокси из объектной ссылки.
Вот серверный код для регистрации хорошо известного типа удаленного объекта в RemotingServices:
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(Hello), // Тип
"Hi", // URI
WellKnownObjectMode.SingleCall); // Режим
Первый аргумент метода RegisterWellKnownServiceType() — Wrox.ProfessionalCSharp.Hello определяет тип удаленного объекта. Второй аргумент — Hi является универсальным идентификатором ресурса удаленного объекта, который используется клиентом для доступа к удаленному объекту. Последний аргумент — режим удаленного объекта. Режим может быть значением перечисления WellKhownObjectMode: SingleCall или Singleton.
□ SingleCall означает, что объект не сохраняет состояние. Для каждого вызова удаленного объекта создается новый экземпляр. Объект SingleCall создается на сервере с помощью метода RemotingConfiguration.RegisterWellKnownServiceТуре() и аргумента WellKnownObjectMode.SingleCall. Это очень эффективно для сервера, так как получается, что не требуется поддерживать ресурсы может быть для тысяч клиентов.
□ С помощью режима Singleton объект совместно используется всеми клиентами сервера. Такие объектные типы могут применяться, если желательно предоставить доступ к некоторым данным всем клиентам. Это не должно быть проблемой для читаемых данных, но для данных чтения-записи необходимо знать о вопросах блокировки и масштабируемости. Объект Singleton создается на сервере с помощью метода RemotingConfiguration.RegisterWellKnownServiceType() и аргумента WellKnownObjectMode.Singleton. Необходимо убедиться, что данные не могут быть повреждены, когда клиенты получают доступ одновременно, но нужно и проверять, что блокирование сделано достаточно эффективно для достижения требуемой масштабируемости.
Сервер для активизированных клиентом объектов
Если удаленный объект должен хранить состояние для определенного клиента, то можно использовать активизированные клиентом объекты. В следующем разделе будут рассмотрены возможности клиентской стороны. В частности, как вызывать объекты, активизированные сервером, и объекты, активизированные клиентом. На серверной стороне активизированные клиентом объекты должны регистрироваться по-другому, чем объекты, активизированные сервером.