Dela via


Duplex Services

Ett duplex-tjänstkontrakt är ett meddelandeutbytesmönster där båda slutpunkterna kan skicka meddelanden till den andra oberoende av varandra. En duplex-tjänst kan därför skicka meddelanden tillbaka till klientslutpunkten och tillhandahålla händelseliknande beteende. Dubbelsidig kommunikation sker när en klient ansluter till en tjänst och ger tjänsten en kanal där tjänsten kan skicka meddelanden tillbaka till klienten. Observera att det händelseliknande beteendet för duplex-tjänster endast fungerar inom en session.

Om du vill skapa ett duplex-kontrakt skapar du ett par gränssnitt. Den första är tjänstkontraktsgränssnittet som beskriver de åtgärder som en klient kan anropa. Det tjänstkontraktet måste ange ett återanropskontrakt i ServiceContractAttribute.CallbackContract egenskapen. Återanropskontraktet är det gränssnitt som definierar de åtgärder som tjänsten kan anropa på klientslutpunkten. Ett duplexkontrakt kräver ingen session, även om duplexbindningarna som tillhandahålls av systemet använder dem.

Följande är ett exempel på ett duplex-kontrakt.

[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

Klassen CalculatorService implementerar det primära ICalculatorDuplex gränssnittet. Tjänsten använder PerSession instansläget för att underhålla resultatet för varje session. En privat egenskap med namnet Callback kommer åt återanropskanalen till klienten. Tjänsten använder återanropet för att skicka tillbaka meddelanden till klienten via motringningsgränssnittet, enligt följande exempelkod.


[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

Klienten måste ange en klass som implementerar återanropsgränssnittet för duplex-kontraktet för att ta emot meddelanden från tjänsten. Följande exempelkod visar en CallbackHandler klass som implementerar ICalculatorDuplexCallback gränssnittet.

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

WCF-klienten som genereras för ett duplex-kontrakt kräver att en InstanceContext klass tillhandahålls vid konstruktion. Den här InstanceContext klassen används som plats för ett objekt som implementerar motringningsgränssnittet och hanterar meddelanden som skickas tillbaka från tjänsten. En InstanceContext klass konstrueras med en instans av CallbackHandler klassen. Det här objektet hanterar meddelanden som skickas från tjänsten till klienten i motringningsgränssnittet.

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

Konfigurationen för tjänsten måste konfigureras för att tillhandahålla en bindning som stöder både sessionskommunikation och duplexkommunikation. Elementet wsDualHttpBinding stöder sessionskommunikation och tillåter dubbelsidig kommunikation genom att tillhandahålla dubbla HTTP-anslutningar, en för varje riktning.

På klienten måste du konfigurera en adress som servern kan använda för att ansluta till klienten, enligt följande exempelkonfiguration.

Kommentar

Icke-duplex-klienter som inte kan autentiseras med en säker konversation genererar vanligtvis en MessageSecurityException. Men om en duplex-klient som använder en säker konversation inte kan autentiseras får klienten en TimeoutException i stället.

Om du skapar en klient/tjänst med elementet WSHttpBinding och inte inkluderar slutpunkten för klientåteranrop får du följande fel.

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

Följande exempelkod visar hur du anger klientens slutpunktsadress programmatiskt.

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

Följande exempelkod visar hur du anger klientens slutpunktsadress i konfigurationen.

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

Varning

Duplex-modellen identifierar inte automatiskt när en tjänst eller klient stänger kanalen. Om en klient oväntat avslutas meddelas tjänsten som standard inte, eller om en tjänst oväntat avslutas, meddelas inte klienten. Om du använder en tjänst som är frånkopplad utlöses undantaget CommunicationException . Klienter och tjänster kan implementera sitt eget protokoll för att meddela varandra om de så önskar. Mer information om felhantering finns i WCF-felhantering.

Se även