WCF NetTcp независимая связь по дуплексному каналу

Я хочу создать дуплексный сервис NetTcp. Я не уверен, что это лучший подход. У меня есть клиент, который периодически отправляет свой статус на сервер, и у меня есть сервер, который должен периодически отправлять данные, которые не зависят от запроса клиента. Поскольку я хочу избежать двух соединений и ничего не знаю о клиентах на сервере, я должен использовать соединение, открытое клиентом. Как я уже сказал, клиент периодически отправляет информацию о состоянии. Но как мне использовать канал соединения, установленный клиентом, независимо для отправки данных клиенту. Кроме того, данные, отправляемые с сервера на клиент, нуждаются в ответе.

[ServiceContract(CallbackContract = typeof(IStatusServiceCallBack))]
public interface IStatusService
{
    [OperationContract]
    void SendStatus(int statusCode, string statusMessage);
}

public interface IStatusServiceCallBack
{
    // I know IsOneWay=true cannot work, but how to return value????
    [OperationContract(IsOneWay = true)]
    int SendPayTransaction(PayTransaction payTransaction);
}

public class StatusService : IStatusService
{
    public IStatusServiceCallBack Proxy
    {
        get
        {
            return OperationContext.Current.GetCallbackChannel
            <IStatusServiceCallBack>();
        }
    }

    public void SendNotification(int statusCode, string statusMessage)
    {
        Console.WriteLine($"\nClient status : {(statusCode)} {statusMessage}");
    }
    // Is this possible???
    public int SendPayTransaction(PayTransaction payTransaction){
        return Proxy.SendPayTransaction(payTransaction)
    }

}


class Program
{
    static void Main(string[] args)
    {
        var svcHost = new ServiceHost(typeof(NotificationService));
        svcHost.Open();
        bool closeService = false;
        do{
            string command = Console.ReadLine();
            if(command == "Send"){
               // Is this possible???????
               int result = svcHost.SendPayTransaction(new PaymentTransaction(){Amount = 5.50});
               Console.WriteLine($"Result of payment : {result}");
            } else if (command == "exit"){
               closeService = true;
            }
        }while(!closeService);
    }
}

1 ответ

Вы правы, что вам нужно использовать соединение, установленное клиентом. Для этого вам нужно выполнить несколько шагов:

  1. Убедитесь, что у вас есть PerSession (или же Singleton) контекстный режим экземпляра для вашего сервиса. PerCall один не позволит вам поддерживать постоянную связь между клиентом и сервером.
  2. Убедиться, что ReceiveTimeout на твоей привязке достаточно долго. Он должен быть длиннее периода между клиентскими запросами, чтобы незанятое соединение не прерывалось инфраструктурой WCF.
  3. В вашем классе обслуживания вы должны помнить канал обратного вызова. Это должно быть сделано в методе, который вызывается клиентом. Итак, результат OperationContext.Current.GetCallbackChannel<IStatusServiceCallBack>() должны быть сохранены в частном поле StatusService когда ты в SendNotification метод. Если ваш сервис имеет PerSession режим экземпляра, тогда у каждого класса обслуживания будет ровно один клиент. В случае Singleton Сервис, вам нужен набор обратных вызовов.
  4. Можно возвращать значение для метода обратного вызова, но для вызова необходим объект канала обратного вызова (сохраненный на шаге 3).
  5. Если вы хотите вызвать метод обратного вызова на всех клиентах, вы должны хранить все обратные вызовы клиентов в статическом поле StatusService а затем вызвать метод для всех из них.
  6. Если вы хотите вызывать метод обратного вызова только для определенного клиента, то вам нужен способ различать клиентов (ID, имя и т. Д.) И сохранять ссылки на каналы обратного вызова в словаре. Если ваш сервис имеет PerSession В режиме контекста экземпляра вы не можете напрямую получить доступ к объекту службы, поэтому все же рекомендуется как-то различать клиентов и обращаться к ним через статическое поле или что-то еще. Вы также можете внедрить свою собственную фабрику для создания сервисных объектов и затем иметь к ним доступ (при условии, что фабрика также где-то регистрирует эти объекты).

Помните, что если клиент отключается, он не делает канал обратного вызова недействительным немедленно. Вы обнаружите это только при вызове метода обратного вызова - исключение будет возвращено.

Другие вопросы по тегам