Udostępnij za pośrednictwem


Usługi dwukierunkowe

Dwukierunkowy kontrakt usługi to wzorzec wymiany komunikatów, w którym oba punkty końcowe mogą wysyłać komunikaty do innych niezależnie. Dlatego usługa dwukierunkowa może wysyłać komunikaty z powrotem do punktu końcowego klienta, zapewniając zachowanie podobne do zdarzeń. Komunikacja dwukierunkowa występuje, gdy klient łączy się z usługą i zapewnia usłudze kanał, w którym usługa może wysyłać komunikaty z powrotem do klienta. Należy pamiętać, że zachowanie podobne do zdarzeń usług dwukierunkowych działa tylko w ramach sesji.

Aby utworzyć kontrakt dwukierunkowy, należy utworzyć parę interfejsów. Pierwszy to interfejs kontraktu usługi, który opisuje operacje, które klient może wywołać. Ten kontrakt usługi musi określać kontrakt wywołania zwrotnego ServiceContractAttribute.CallbackContract we właściwości . Kontrakt wywołania zwrotnego to interfejs definiujący operacje, które usługa może wywołać w punkcie końcowym klienta. Kontrakt dwukierunkowy nie wymaga sesji, chociaż powiązania dwukierunkowe dostarczane przez system korzystają z nich.

Poniżej przedstawiono przykład kontraktu dwustronnego.

[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

Klasa CalculatorService implementuje interfejs podstawowy ICalculatorDuplex . Usługa używa PerSession trybu wystąpienia, aby zachować wynik dla każdej sesji. Właściwość prywatna o nazwie Callback uzyskuje dostęp do kanału wywołania zwrotnego do klienta. Usługa używa wywołania zwrotnego do wysyłania komunikatów z powrotem do klienta za pośrednictwem interfejsu wywołania zwrotnego, jak pokazano w poniższym przykładowym kodzie.


[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 musi podać klasę, która implementuje interfejs wywołania zwrotnego kontraktu dwustronnego na potrzeby odbierania komunikatów z usługi. Poniższy przykładowy kod przedstawia klasę CallbackHandler , która implementuje ICalculatorDuplexCallback interfejs.

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 generowany na potrzeby kontraktu dwudupleksowego InstanceContext wymaga podania klasy podczas budowy. Ta InstanceContext klasa jest używana jako lokacja obiektu, który implementuje interfejs wywołania zwrotnego i obsługuje komunikaty wysyłane z powrotem z usługi. Klasa InstanceContext jest konstruowana z wystąpieniem CallbackHandler klasy. Ten obiekt obsługuje komunikaty wysyłane z usługi do klienta w interfejsie wywołania zwrotnego.

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

Konfiguracja usługi musi zostać skonfigurowana w celu zapewnienia powiązania obsługującego komunikację sesji i komunikację dwukierunkową. Element wsDualHttpBinding obsługuje komunikację sesji i umożliwia komunikację dwukierunkową, zapewniając podwójne połączenia HTTP, po jednym dla każdego kierunku.

Na kliencie należy skonfigurować adres, za pomocą którego serwer może nawiązać połączenie z klientem, jak pokazano w poniższej przykładowej konfiguracji.

Uwaga

Klienci niedupleksowi, którzy nie mogą uwierzytelniać się przy użyciu bezpiecznej konwersacji, zwykle zgłaszają błąd MessageSecurityException. Jeśli jednak klient dwudupleksowy korzystający z bezpiecznej konwersacji nie może uwierzytelnić się, klient otrzymuje TimeoutException zamiast tego.

Jeśli tworzysz klienta/usługę przy użyciu WSHttpBinding elementu i nie dołączysz punktu końcowego wywołania zwrotnego klienta, zostanie wyświetlony następujący błąd.

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

Poniższy przykładowy kod pokazuje, jak programowo określić adres punktu końcowego 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/")

Poniższy przykładowy kod pokazuje, jak określić adres punktu końcowego klienta w konfiguracji.

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

Ostrzeżenie

Model dwukierunkowy nie wykrywa automatycznie, kiedy usługa lub klient zamyka kanał. Dlatego jeśli klient nieoczekiwanie zakończy działanie, domyślnie usługa nie zostanie powiadomiona lub jeśli usługa niespodziewanie zakończy działanie, klient nie zostanie powiadomiony. Jeśli używasz usługi, która jest rozłączona, CommunicationException zostanie zgłoszony wyjątek. Klienci i usługi mogą zaimplementować własny protokół, aby powiadomić siebie nawzajem, jeśli tak wybierzą. Aby uzyskać więcej informacji na temat obsługi błędów, zobacz Obsługa błędów WCF.

Zobacz też