Condividi tramite


Servizi duplex

Un contratto di servizio duplex è un modello di scambio di messaggi in cui entrambi gli endpoint possono inviare messaggi all'altro in modo indipendente. Un servizio duplex, pertanto, può inviare messaggi all'endpoint client, con un comportamento simile a quello di un evento. La comunicazione duplex si verifica quando un client si connette a un servizio e fornisce al servizio un canale in cui il servizio può inviare messaggi al client. Si noti che il comportamento tipo evento dei servizi duplex funziona solo durante una sessione.

Per creare un contratto duplex, si crea una coppia di interfacce. Il primo è l'interfaccia del contratto di servizio che descrive le operazioni che un client può richiamare. Tale contratto di servizio deve specificare un contratto di callback nella ServiceContractAttribute.CallbackContract proprietà . Il contratto di callback è l'interfaccia che definisce le operazioni che il servizio può chiamare sull'endpoint client. Un contratto duplex non richiede una sessione, anche se le associazioni duplex fornite dal sistema ne fanno uso.

Di seguito è riportato un esempio di contratto 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

La CalculatorService classe implementa l'interfaccia primaria ICalculatorDuplex . Il servizio utilizza la modalità istanza PerSession per mantenere il risultato di ogni sessione. Una proprietà privata chiamata Callback accede al client tramite il canale di callback. Il servizio usa il callback per l'invio di messaggi al client tramite l'interfaccia di callback, come illustrato nel codice di esempio seguente.


[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

Il client deve fornire una classe che implementa l'interfaccia di callback del contratto duplex per la ricezione di messaggi dal servizio. Il codice di esempio seguente illustra una CallbackHandler classe che implementa l'interfaccia 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

La classe InstanceContext deve essere fornita durante la costruzione di un client WCF generato per un contratto duplex. Questa InstanceContext classe viene utilizzata come sito per un oggetto che implementa l'interfaccia di callback e gestisce i messaggi inviati dal servizio. Una InstanceContext classe viene costruita con un'istanza della CallbackHandler classe . Questo oggetto gestisce i messaggi inviati dal servizio al client nell'interfaccia di callback.

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

La configurazione per il servizio deve essere configurata per fornire un'associazione che supporta la comunicazione di sessione e la comunicazione duplex. L'elemento wsDualHttpBinding supporta la comunicazione di sessione e consente la comunicazione duplex fornendo connessioni HTTP doppie, una per ogni direzione.

Nel client è necessario configurare un indirizzo che il server può usare per connettersi al client, come illustrato nella configurazione di esempio seguente.

Annotazioni

I client non duplex che non riescono a eseguire l'autenticazione utilizzando una conversazione sicura di solito generano un'eccezione MessageSecurityException. Tuttavia, se un client duplex che utilizza una conversazione sicura fallisce nell'autenticazione, il client riceve invece TimeoutException.

Se si crea un client/servizio usando l'elemento WSHttpBinding e non si include l'endpoint di callback client, verrà visualizzato l'errore seguente.

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

Il codice di esempio seguente illustra come specificare l'indirizzo dell'endpoint client a livello di codice.

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

Il codice di esempio seguente illustra come specificare l'indirizzo dell'endpoint client nella configurazione.

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

Avvertimento

Il modello duplex non rileva automaticamente quando un servizio o un client chiude il canale. Pertanto, se un client termina in modo imprevisto, per impostazione predefinita il servizio non riceverà alcuna notifica o se un servizio termina in modo imprevisto, il client non riceverà alcuna notifica. Se si usa un servizio disconnesso, viene generata l'eccezione CommunicationException . I client e i servizi possono implementare il proprio protocollo per notificarsi tra loro, se lo scelgono. Per altre informazioni sulla gestione degli errori, vedere Gestione degli errori WCF.

Vedere anche