Serviços Duplex

Um contrato de serviço duplex é um padrão de troca de mensagens no qual ambos os pontos de extremidade podem enviar mensagens para o outro de forma independente. Um serviço duplex, portanto, pode enviar mensagens de volta para o ponto de extremidade do cliente, fornecendo comportamento semelhante a um evento. A comunicação duplex ocorre quando um cliente se conecta a um serviço e fornece ao serviço um canal no qual o serviço pode enviar mensagens de volta ao cliente. Observe que o comportamento semelhante a um evento dos serviços duplex só funciona dentro de uma sessão.

Para criar um contrato duplex, crie um par de interfaces. A primeira é a interface do contrato de serviço que descreve as operações que um cliente pode invocar. Esse contrato de serviço deve especificar um contrato de retorno de ServiceContractAttribute.CallbackContract chamada na propriedade. O contrato de retorno de chamada é a interface que define as operações que o serviço pode chamar no ponto de extremidade do cliente. Um contrato duplex não requer uma sessão, embora as ligações duplex fornecidas pelo sistema façam uso delas.

Segue-se um exemplo de um contrato duplex.

[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

A CalculatorService classe implementa a interface primária ICalculatorDuplex . O serviço usa o PerSession modo de instância para manter o resultado de cada sessão. Uma propriedade privada chamada Callback acessa o canal de retorno de chamada para o cliente. O serviço usa o retorno de chamada para enviar mensagens de volta ao cliente por meio da interface de retorno de chamada, conforme mostrado no código de exemplo a seguir.


[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

O cliente deve fornecer uma classe que implemente a interface de retorno de chamada do contrato duplex, para receber mensagens do serviço. O código de exemplo a seguir mostra uma CallbackHandler classe que implementa a ICalculatorDuplexCallback interface.

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

    public void Equation(string eqn)
    {
        Console.WriteLine("Equation({0})", 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

O cliente WCF que é gerado para um contrato duplex requer uma InstanceContext classe a ser fornecida durante a construção. Essa InstanceContext classe é usada como o site para um objeto que implementa a interface de retorno de chamada e manipula mensagens que são enviadas de volta do serviço. Uma InstanceContext classe é construída com uma instância da CallbackHandler classe. Este objeto lida com mensagens enviadas do serviço para o cliente na interface de retorno de chamada.

// 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)

A configuração para o serviço deve ser configurada para fornecer uma ligação que suporte a comunicação de sessão e comunicação duplex. O wsDualHttpBinding elemento suporta comunicação de sessão e permite comunicação duplex fornecendo conexões HTTP duplas, uma para cada direção.

No cliente, você deve configurar um endereço que o servidor pode usar para se conectar ao cliente, conforme mostrado na configuração de exemplo a seguir.

Nota

Os clientes não duplex que não conseguem autenticar usando uma conversa segura normalmente lançam um MessageSecurityExceptionarquivo . No entanto, se um cliente duplex que usa uma conversa segura não conseguir autenticar, o cliente receberá um TimeoutException em vez disso.

Se você criar um cliente/serviço usando o WSHttpBinding elemento e não incluir o ponto de extremidade de retorno de chamada do cliente, receberá o seguinte erro.

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

O código de exemplo a seguir mostra como especificar o endereço do ponto de extremidade do cliente programaticamente.

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/")

O código de exemplo a seguir mostra como especificar o endereço do ponto de extremidade do cliente na configuração.

<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>

Aviso

O modelo duplex não deteta automaticamente quando um serviço ou cliente fecha seu canal. Portanto, se um cliente encerrar inesperadamente, por padrão, o serviço não será notificado, ou se um serviço for encerrado inesperadamente, o cliente não será notificado. Se você usar um serviço desconectado, a CommunicationException exceção será gerada. Clientes e serviços podem implementar seu próprio protocolo para notificar uns aos outros, se assim desejarem. Para obter mais informações sobre tratamento de erros, consulte Tratamento de erros WCF.

Consulte também