Страницы

Поиск по вопросам

пятница, 26 октября 2018 г.

Как WCF служба может узнать, что клиент отсоединился?

Когда клиент хочет отредактировать запись, то шлется запрос WCF службе, что бы она в свою очередь обновила статус этой записи в БД на "редактируется", что бы другие клиенты с ней ничего не могли сделать на время редактирования.
Так вот, если неожиданно клиент отрубился(пропал интернет и все такое), как WCF служба может узнать, что клиента, который производил редактирование, больше нет и нужно вернуть статус на доступно?
А что если деструктор на стороне службы сделать? По идее если клиент не отвечает долгое время, то его экземпляр умирает и тут в дело приходит мой деструктор , который разблокирует записи, если такие есть


Ответ

Для этих целей лучше использовать класс IChannelInitializer - класс, предназначенный для детектирования подключения клиентов, в котором вы можете установить события на закрытие канала(чтобы не писать их в каждой реализации метода службы)
Например, создадим класс ClientTrackerChannelInitializer, который наследует интерфейс IChannelInitializer
class ClientTrackerChannelInitializer : IChannelInitializer { internal static int ConnectedClientCount = 0;
// метод, который определяет, что клиент подсоединился, создался новый канал public void Initialize(IClientChannel channel) { ConnectedClientCount++; Console.WriteLine("Client {0} initialized", channel.SessionId); channel.Closed += ClientDisconnected; channel.Faulted += ClientDisconnected; } // событие, на которое подписались при создании канала static void ClientDisconnected(object sender, EventArgs e) { Console.WriteLine("Client {0} disconnected", ((IClientChannel)sender).SessionId); ConnectedClientCount--; } }
У него два метода
Initialize - метод, который срабатывает при подключении нового клиента ClientDisconnected - метод, обработчик события закрытия канала(или ошибки в канале)
Теперь необходимо подключить класс ClientTrackerChannelInitializer на стороне сервера. Для этого создадим класс, реализующий интерфейс IEndpointBehavior, класс, который позволяет расширять поведение конечной точки службы
class ClientTrackerEndpointBehavior : IEndpointBehavior { public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { }
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { // подключаем наш класс endpointDispatcher.ChannelDispatcher.ChannelInitializers.Add(new ClientTrackerChannelInitializer()); }
public void Validate(ServiceEndpoint endpoint) { } }
В методе ApplyDispatchBehavior мы подключили класс ClientTrackerChannelInitializer. Все классы созданы. Теперь можно публиковать службу. Предположим, что у нас есть сервис IStackCalculator. Класс StackCalculator реализует данный сервис
[ServiceContract(SessionMode = SessionMode.Required)] public interface IStackCalculator { [OperationContract] void Enter(double value); [OperationContract] double Add(); [OperationContract] double Subtract(); [OperationContract] double Multiply(); [OperationContract] double Divide(); }
Теперь при публикации хоста сервиса подключим наше расширение конечной точки.
string baseAddress = "http://" + Environment.MachineName + ":8000/Service"; ServiceHost host = new ServiceHost(typeof(StackCalculator), new Uri(baseAddress)); WSHttpBinding binding = new WSHttpBinding(SecurityMode.None); binding.ReliableSession.Enabled = true; ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(IStackCalculator), binding, ""); // подключаем расширение конечной точки службы endpoint.Behaviors.Add(new ClientTrackerEndpointBehavior()); host.Open(); Console.WriteLine("Host opened");
Теперь при соединении/отключении клиентов будут срабатывать события Initialize и ClientDisconnected нашего класса ClientTrackerChannelInitialize. Нет необходимости прописывать подписывание на событие закрытия канала в самой реализации метода.
Механизм создания классов-расширения служб, реализующих интерфейс IEndpointBehaviour, можно применять для многих целей. По своему опыту применял для подсчета количества вызовов функций сервиса, для замера скорости выполнения функции сервиса, для аутентификации пользователя... Посмотрите Примеры использования IEndpointBehavior.
Более подробно о примере, который мы рассмотрели, можно прочитать здесь Пример кода
P.S. Если необходимо подключить кастомный IEndpointBehavior через файл config, напишите, добавлю в ответ.
Edit: Чтобы подключить наш класс-расширение конечной точки ClientTrackerEndpointBehavior, мы должны сделать следующее
Зарегистрировать наше поведение в < system.servicemodel >/< extensions >/< behaviorExtensions > указав имя расширения и тип класса, реализующего расширение(тип класса ClientTrackerEndpointBehavior)
< extensions > < behaviorExtensions> < add name="clientTracker" type="ClientTrackerEndpointBehavior, Client.ExtensionsDLL /> < /behaviorExtensions> Добавить расширение конечной точки в элементе < behaviors >. Здесь мы в качестве расширения указываем элемент-расширение clientTracker, которое определили на предыдущем шаге
< behaviors> < endpointBehaviors> < behavior name="ServiceBehaviorExtension"> < clientTracker /> < /behavior> < /endpointBehaviors> < /behaviors> Теперь при определении конечной точки сервиса мы можем подключить поведение через атрибут behaviourConfiguration, указав имя расширения, указанного выше
< services> < service name="StackCalculator" > < endpoint name="endpoint1" address="" binding="basicHttpBinding" bindingName="binding" contract="IStackCalculator" behaviorConfiguration="ServiceBehaviorExtension" / > ... < /services >
Пример можно посмотреть например отсюда

Комментариев нет:

Отправить комментарий