Duplexní služby

Duplexní kontrakt služby je vzor výměny zpráv, ve kterém oba koncové body mohou posílat zprávy druhému nezávisle. Duplexní služba proto může posílat zprávy zpět do koncového bodu klienta a poskytovat chování podobné událostem. Duplexní komunikace nastane, když se klient připojí ke službě a poskytne službě kanál, na kterém může služba odesílat zprávy zpět klientovi. Všimněte si, že chování duplexních služeb podobné události funguje pouze v rámci relace.

Pokud chcete vytvořit duplexní kontrakt, vytvoříte dvojici rozhraní. První je rozhraní kontraktu služby, které popisuje operace, které může klient vyvolat. Tento kontrakt služby musí ve vlastnosti zadat kontrakt zpětného ServiceContractAttribute.CallbackContract volání. Kontrakt zpětného volání je rozhraní, které definuje operace, které může služba volat na koncový bod klienta. Duplexní kontrakt nevyžaduje relaci, i když systémové duplexní vazby využívají.

Následuje příklad duplexního kontraktu.

[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

Třída CalculatorService implementuje primární ICalculatorDuplex rozhraní. Služba používá PerSession režim instance k zachování výsledku pro každou relaci. Privátní vlastnost s názvem Callback přistupuje ke kanálu zpětného volání klientovi. Služba používá zpětné volání pro odesílání zpráv zpět klientovi prostřednictvím rozhraní zpětného volání, jak je znázorněno v následujícím vzorovém kódu.


[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

Klient musí poskytnout třídu, která implementuje rozhraní zpětného volání duplexního kontraktu pro příjem zpráv ze služby. Následující ukázkový kód ukazuje CallbackHandler třídu, která implementuje ICalculatorDuplexCallback rozhraní.

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

Klient WCF vygenerovaný pro duplexní kontrakt vyžaduje, aby byla při sestavování poskytována InstanceContext třída. Tato InstanceContext třída se používá jako lokalita pro objekt, který implementuje rozhraní zpětného volání a zpracovává zprávy odeslané zpět ze služby. Třída InstanceContext je vytvořena s instancí CallbackHandler třídy. Tento objekt zpracovává zprávy odeslané ze služby klientovi v rozhraní zpětného volání.

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

Konfigurace služby musí být nastavená tak, aby poskytovala vazbu, která podporuje komunikaci relace i duplexní komunikaci. Element wsDualHttpBinding podporuje komunikaci relace a umožňuje duplexní komunikaci tím, že poskytuje duální připojení HTTP, jednu pro každý směr.

V klientovi musíte nakonfigurovat adresu, kterou server může použít pro připojení k klientovi, jak je znázorněno v následující ukázkové konfiguraci.

Poznámka:

Ne duplexní klienti, kteří se nepodaří ověřit pomocí zabezpečené konverzace, obvykle vyvolá MessageSecurityExceptionchybu . Pokud se ale oboustranný klient, který používá zabezpečenou konverzaci, nepodaří ověřit, klient místo toho obdrží TimeoutException .

Pokud vytvoříte klienta nebo službu pomocí elementu WSHttpBinding a nezahrnete koncový bod zpětného volání klienta, zobrazí se následující chyba.

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

Následující ukázkový kód ukazuje, jak programově zadat adresu koncového bodu klienta.

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

Následující ukázkový kód ukazuje, jak zadat adresu koncového bodu klienta v konfiguraci.

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

Upozorňující

Duplexní model automaticky nezjistí, kdy služba nebo klient zavře svůj kanál. Takže pokud se klient neočekávaně ukončí, služba ve výchozím nastavení nebude upozorněna nebo pokud se služba neočekávaně ukončí, klient nebude upozorněn. Pokud používáte službu, která je odpojena, je CommunicationException vyvolána výjimka. Klienti a služby můžou implementovat svůj vlastní protokol, aby se navzájem informovali, pokud tak zvolí. Další informace o zpracování chyb naleznete v tématu Zpracování chyb WCF.

Viz také