Поделиться через


Дуплексные службы

Дуплексный контракт службы — это шаблон обмена сообщениями, в котором обе конечные точки могут отправлять сообщения другим независимо. Таким образом, дуплексная служба может отправлять сообщения обратно в конечную точку клиента, обеспечивая поведение, подобное событиям. Дуплексное взаимодействие происходит, когда клиент подключается к службе и предоставляет службе канал, на котором служба может отправлять сообщения обратно клиенту. Обратите внимание, что поведение дуплексных служб, похожее на события, работает только в рамках сеанса.

Чтобы создать дуплексный контракт, создайте пару интерфейсов. Первым является интерфейс контракта службы, описывающий операции, которые могут вызываться клиентом. Этот контракт службы должен указывать контракт обратного вызова в свойстве ServiceContractAttribute.CallbackContract . Контракт обратного вызова — это интерфейс, определяющий операции, которые служба может вызывать в конечной точке клиента. Дуплексный контракт не требует сеанса, хотя предоставляемые системой дуплексные привязки используют их.

Ниже приведен пример дуплексного контракта.

[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples", SessionMode=SessionMode.Required,
                 CallbackContract=typeof(ICalculatorDuplexCallback))]
public interface ICalculatorDuplex
{
    [OperationContract(IsOneWay = true)]
    void Clear();
    [OperationContract(IsOneWay = true)]
    void AddTo(double n);
    [OperationContract(IsOneWay = true)]
    void SubtractFrom(double n);
    [OperationContract(IsOneWay = true)]
    void MultiplyBy(double n);
    [OperationContract(IsOneWay = true)]
    void DivideBy(double n);
}

public interface ICalculatorDuplexCallback
{
    [OperationContract(IsOneWay = true)]
    void Equals(double result);
    [OperationContract(IsOneWay = true)]
    void Equation(string eqn);
}
<ServiceContract(Namespace:="http://Microsoft.ServiceModel.Samples", SessionMode:=SessionMode.Required, CallbackContract:=GetType(ICalculatorDuplexCallback))> _
Public Interface ICalculatorDuplex
    <OperationContract(IsOneWay:=True)> _
    Sub Clear()
    <OperationContract(IsOneWay:=True)> _
    Sub AddTo(ByVal n As Double)
    <OperationContract(IsOneWay:=True)> _
    Sub SubtractFrom(ByVal n As Double)
    <OperationContract(IsOneWay:=True)> _
    Sub MultiplyBy(ByVal n As Double)
    <OperationContract(IsOneWay:=True)> _
    Sub DivideBy(ByVal n As Double)
End Interface


Public Interface ICalculatorDuplexCallback
    <OperationContract(IsOneWay:=True)> _
    Sub Equals(ByVal result As Double)
    <OperationContract(IsOneWay:=True)> _
    Sub Equation(ByVal eqn As String)
End Interface

Класс CalculatorService реализует первичный ICalculatorDuplex интерфейс. Служба использует режим экземпляра PerSession для сохранения результата для каждого сеанса. Частное свойство с именем Callback получает доступ к каналу обратного вызова клиента. Служба использует обратный вызов для отправки сообщений клиенту через интерфейс обратного вызова, как показано в следующем примере кода.


[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class CalculatorService : ICalculatorDuplex
{
    double result = 0.0D;
    string equation;

    public CalculatorService()
    {
        equation = result.ToString();
    }

    public void Clear()
    {
        Callback.Equation(equation + " = " + result.ToString());
        equation = result.ToString();
    }

    public void AddTo(double n)
    {
        result += n;
        equation += " + " + n.ToString();
        Callback.Equals(result);
    }

    public void SubtractFrom(double n)
    {
        result -= n;
        equation += " - " + n.ToString();
        Callback.Equals(result);
    }

    public void MultiplyBy(double n)
    {
        result *= n;
        equation += " * " + n.ToString();
        Callback.Equals(result);
    }

    public void DivideBy(double n)
    {
        result /= n;
        equation += " / " + n.ToString();
        Callback.Equals(result);
    }

    ICalculatorDuplexCallback Callback
    {
        get
        {
            return OperationContext.Current.GetCallbackChannel<ICalculatorDuplexCallback>();
        }
    }
}

<ServiceBehavior(InstanceContextMode:=InstanceContextMode.PerSession)> _
Public Class CalculatorService
    Implements ICalculatorDuplex
    Private result As Double = 0.0R
    Private equation As String

    Public Sub New()
        equation = result.ToString()
    End Sub

    Public Sub Clear() Implements ICalculatorDuplex.Clear
        Callback.Equation(equation & " = " & result.ToString())
        equation = result.ToString()
    End Sub

    Public Sub AddTo(ByVal n As Double) Implements ICalculatorDuplex.AddTo
        result += n
        equation &= " + " & n.ToString()
        CType(Callback, Object).Equals(result)
    End Sub

    Public Sub SubtractFrom(ByVal n As Double) Implements ICalculatorDuplex.SubtractFrom
        result -= n
        equation &= " - " & n.ToString()
        CType(Callback, Object).Equals(result)
    End Sub

    Public Sub MultiplyBy(ByVal n As Double) Implements ICalculatorDuplex.MultiplyBy
        result *= n
        equation &= " * " & n.ToString()
        CType(Callback, Object).Equals(result)
    End Sub

    Public Sub DivideBy(ByVal n As Double) Implements ICalculatorDuplex.DivideBy
        result /= n
        equation &= " / " & n.ToString()
        CType(Callback, Object).Equals(result)
    End Sub

    Private ReadOnly Property Callback() As ICalculatorDuplexCallback
        Get
            Return OperationContext.Current.GetCallbackChannel(Of ICalculatorDuplexCallback)()
        End Get
    End Property
End Class

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

public class CallbackHandler : ICalculatorDuplexCallback
{
    public void Equals(double result)
    {
        Console.WriteLine($"Equals({result})");
    }

    public void Equation(string eqn)
    {
        Console.WriteLine($"Equation({eqn})");
    }
}
Public Class CallbackHandler
    Implements ICalculatorDuplexCallback
    Public Overridable Shadows Sub Equals(ByVal result As Double) Implements ICalculatorDuplexCallback.Equals
        Console.WriteLine("Equals({0})", result)
    End Sub

    Public Sub Equation(ByVal eqn As String) Implements ICalculatorDuplexCallback.Equation
        Console.WriteLine("Equation({0})", eqn)
    End Sub
End Class

Клиент WCF, созданный для дуплексного контракта, требует InstanceContext предоставления класса при построении. Этот InstanceContext класс используется в качестве сайта для объекта, реализующего интерфейс обратного вызова, и обрабатывает сообщения, отправляемые обратно из службы. Класс InstanceContext создается с экземпляром CallbackHandler класса. Этот объект обрабатывает сообщения, отправленные из службы клиенту в интерфейс обратного вызова.

// Construct InstanceContext to handle messages on callback interface
InstanceContext instanceContext = new InstanceContext(new CallbackHandler());

// Create a client
CalculatorDuplexClient client = new CalculatorDuplexClient(instanceContext);
' Construct InstanceContext to handle messages on callback interface
Dim instanceContext As New InstanceContext(New CallbackHandler())

' Create a client
Dim client As New CalculatorDuplexClient(instanceContext)

Для настройки конфигурации службы необходимо задать связывание, которое поддерживает как сессионное взаимодействие, так и дуплексную связь. Элемент wsDualHttpBinding поддерживает обмен данными сеансов и обеспечивает дуплексное взаимодействие, предоставляя два HTTP-подключения, по одному для каждого направления.

На клиенте необходимо настроить адрес, который сервер может использовать для подключения к клиенту, как показано в следующем примере конфигурации.

Примечание.

Недуплексные клиенты, не прошедшие проверку подлинности с помощью защищенного сеанса, обычно вызывают MessageSecurityException исключение. Однако если дуплексный клиент, использующий защищенный диалог, не проходит проверку подлинности, он получает TimeoutException вместо этого.

Если вы создаете клиент или службу с помощью WSHttpBinding элемента и не включаете конечную точку обратного вызова клиента, вы получите следующую ошибку.

HTTP could not register URL
htp://+:80/Temporary_Listen_Addresses/<guid> because TCP port 80 is being used by another application.

В следующем примере кода показано, как программно указать адрес конечной точки клиента.

WSDualHttpBinding binding = new WSDualHttpBinding();
EndpointAddress endptadr = new EndpointAddress("http://localhost:12000/DuplexTestUsingCode/Server");
binding.ClientBaseAddress = new Uri("http://localhost:8000/DuplexTestUsingCode/Client/");
Dim binding As New WSDualHttpBinding()
Dim endptadr As New EndpointAddress("http://localhost:12000/DuplexTestUsingCode/Server")
binding.ClientBaseAddress = New Uri("http://localhost:8000/DuplexTestUsingCode/Client/")

В следующем примере кода показано, как указать адрес конечной точки клиента в конфигурации.

<client>
    <endpoint name ="ServerEndpoint"
          address="http://localhost:12000/DuplexTestUsingConfig/Server"
          bindingConfiguration="WSDualHttpBinding_IDuplexTest"
            binding="wsDualHttpBinding"
           contract="IDuplexTest" />
</client>
<bindings>
    <wsDualHttpBinding>
        <binding name="WSDualHttpBinding_IDuplexTest"
          clientBaseAddress="http://localhost:8000/myClient/" >
            <security mode="None"/>
         </binding>
    </wsDualHttpBinding>
</bindings>

Предупреждение

Дуплексная модель не обнаруживает автоматически, когда служба или клиент закрывает свой канал. Таким образом, если клиент неожиданно завершает работу, по умолчанию служба не будет уведомлена или если служба неожиданно завершает работу, клиент не будет уведомлен. Если вы используете отключённую службу, возникает исключение CommunicationException. Клиенты и службы могут реализовать собственный протокол, чтобы уведомить друг друга, если они решили. Дополнительные сведения об обработке ошибок см. в разделе "Обработка ошибок WCF".

См. также