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


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

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

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

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

<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
[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);
}

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

<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
[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>();
        }
    }
}

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

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
public class CallbackHandler : ICalculatorDuplexCallback
{
    public void Equals(double result)
    {
        Console.WriteLine("Equals({0})", result);
    }

    public void Equation(string eqn)
    {
        Console.WriteLine("Equation({0})", eqn);
    }
}

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

' Construct InstanceContext to handle messages on callback interface
Dim instanceContext As New InstanceContext(New CallbackHandler())

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

// Create a client
CalculatorDuplexClient client = new CalculatorDuplexClient(instanceContext);

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

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

ms731064.note(ru-ru,VS.100).gifПримечание
Недуплексные клиенты, которые не смогли пройти проверку с помощью защищенного диалога, как правило, создают исключение 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("https://localhost:12000/DuplexTestUsingCode/Server");
binding.ClientBaseAddress = new Uri("https://localhost:8000/DuplexTestUsingCode/Client/");

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

<client>
    <endpoint name ="ServerEndpoint" 
          address="https://localhost:12000/DuplexTestUsingConfig/Server"
          bindingConfiguration="WSDualHttpBinding_IDuplexTest" 
            binding="wsDualHttpBinding"
           contract="IDuplexTest" />
</client>
<bindings>
    <wsDualHttpBinding>
        <binding name="WSDualHttpBinding_IDuplexTest"  
          clientBaseAddress="https://localhost:8000/myClient/" >
            <security mode="None"/>
         </binding>
    </wsDualHttpBinding>
</bindings>
ms731064.Warning(ru-ru,VS.100).gif Внимание!
Дуплексная модель не выполняет автоматического обнаружения закрытия службой или клиентом своего канала. Поэтому в случае неожиданного завершения работы службы или клиента партнер не будет уведомлен по умолчанию. Клиенты и службы могут реализовать собственный протокол для уведомления друг друга по усмотрению.

См. также

Задачи

Дуплекс
Как создать фабрику каналов и использовать ее для создания каналов и управления ими

Основные понятия

Задание поведения клиента во время выполнения