Сервер
Сервер реализуется внутри консольного приложения. Мы ожидаем только, чтобы пользователь завершил работу сервера после чтения конфигурационного файла и настройки канала и удаленного объекта:
using System;
using System.Runtime.Remoting;
namespace Wrox.ProfessionalCSharp {
class Server {
static void Main(string[] args) {
RemotingConfiguration.Configure('Server.exe.config');
Console.WriteLine('Hit to exit');
Console.ReadLine();
}
}
}
Способ создания конфигурационного файла сервера Server.exe.config
мы уже обсуждали. Существует только один важный момент. Так как клиент сначала регистрирует обработчик событий и после этого вызывает удаленный метод, то удаленный объект должен сохранять состояние клиента. Можно использовать с событиями объекты SingleCall
, поэтому класс RemoteObject
конфигурируется в виде активированного клиентом типа:
<configuration>
<system.runtime.remoting>
<application name='CallbackSample'>
<service>
<activated type='Wrox.ProfessionalCSharp.RemoteObject, RemoteObject' />
</service>
<channels>
<channel type='System.Runtime.Remoting.Channels.Http.HttpChannel, System.Runtime.Remoting' port='6791' />
</channels>
</application>
</system.runtime.remoting>
</configuration>
Приемник событий
Приемник событий реализует обработчик StatusHandler()
, который определен в делегате. Как ранее отмечалось, метод может иметь только входные параметры и возвращать только void
. Это в точности соответствует требованиям методов [OneWay]
, как мы видели ранее при рассмотрении асинхронной удаленной работы. StatusHandler()
будет вызываться асинхронно. Класс EventSink
должен также наследовать из класса MarshalByRefObject
, чтобы сделать его удаленным, так как он будет вызывать с сервера удаленным образом:
using System;
using System.Runtime.Remoting.Messaging;
namespace Wrox.ProfessionalCSharp; {
public class EventSink MarshalByRefObject {
public EventSink() { }
[OneWay]
public void StatusHandler(object sender, StatusEventArgs e) {
Сonsole.WriteLine('EventSink: Event occurred: ' + e.Message);
}
}
}
Клиент
Клиент читает конфигурационный файл клиента с помощью класса RemotingConfiguration
. Так было со всеми клиентами, которые создавались до сих пор. Клиент создает локально экземпляр удаленного класса приемника EventSink
. Метод, который должен вызываться из удаленного объекта на сервере, передается в удаленный объект:
using System;
using System.Runtime.Remoting;
namespace Wrox.ProfessionalCSharp {
class Client {
static void Main(string[] args) {
RemotingConfiguration.Configure('Client.exe.config');
Различие начинается здесь. Мы должны создать локально экземпляр удаленного класса приемника EventSink
. Так как этот класс не будет конфигурироваться элементом <client>
, то его экземпляр создается локально. Затем мы получаем экземпляр класса удаленного объекта RemoteObject
. Этот класс конфигурируется в элементе <client>
, поэтому его экземпляр создается на удаленном сервере:
EventSink sink = new EventSink();
RemoteObject obj = new RemoteObject();
Теперь можно зарегистрировать метод обработчика объекта EventSink
на удаленном объекте. StatusEvent
является именем делегата, который был определен на сервере. Метод StatusHandler()
имеет те же самые аргументы, которые определены в StatusEvent
.
Вызывая метод LongWorking()
, сервер будет делать обратный вызов в методе StatusHandler()
в начале и в конце метода:
// зарегистрировать клиентский приемник на сервере — подписаться
// на событие
obj.Status += new StatusEvent(sink.StatusHandler);
obj.LongWorking(5000);
Теперь мы более не заинтересованы в получении событий с сервера и отменяем подписку на событие. Следующий раз при вызове LongWorking()
никакие события не будут получены.
// отменить подписку на событие
obj.Status -= new StatusEvent(sink.StatusHandler);
obj.LongWorking(5000);
Console.WriteLine('Hit to exit');
Console.ReadLine();