Partager via


Duplex Services

Un contrat de service duplex est un modèle d’échange de messages dans lequel les deux points de terminaison peuvent envoyer des messages à l’autre indépendamment. Par conséquent, un service duplex peut retransmettre des messages au point de terminaison client, offrant un comportement similaire à un événement. La communication duplex se produit lorsqu’un client se connecte à un service et fournit au service un canal sur lequel le service peut renvoyer des messages au client. Notez que le comportement de type événement des services duplex fonctionne uniquement dans une session.

Pour créer un contrat duplex, vous créez une paire d’interfaces. La première est l’interface de contrat de service qui décrit les opérations qu’un client peut appeler. Ce contrat de service doit spécifier un contrat de rappel dans la ServiceContractAttribute.CallbackContract propriété. Le contrat de rappel est l’interface qui définit les opérations que le service peut appeler sur le point de terminaison client. Un contrat duplex ne requiert pas de session, bien que les liaisons duplex fournies par le système en utilisent.

Voici un exemple de contrat 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 implémente l’interface principale ICalculatorDuplex . Le service utilise le mode d’instance PerSession pour conserver le résultat de chaque session. Une propriété privée appelée Callback accède au canal de rappel au client. Le service utilise le rappel pour renvoyer des messages au client via l’interface de rappel, comme indiqué dans l’exemple de code suivant.


[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

Le client doit fournir une classe qui implémente l’interface de rappel du contrat duplex, pour recevoir des messages du service. L’exemple de code suivant montre une CallbackHandler classe qui implémente l’interface 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

Le client WCF généré pour un contrat duplex nécessite qu’une InstanceContext classe soit fournie lors de la construction. Cette InstanceContext classe est utilisée comme site pour un objet qui implémente l’interface de rappel et gère les messages renvoyés par le service. Une InstanceContext classe est construite avec une instance de la CallbackHandler classe. Cet objet gère les messages envoyés du service au client sur l’interface de rappel.

// 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 configuration du service doit être configurée pour fournir une liaison qui prend en charge à la fois la communication de session et la communication duplex. L’élément prend en charge la wsDualHttpBinding communication de session et autorise la communication duplex en fournissant des connexions HTTP doubles, une pour chaque direction.

Sur le client, vous devez configurer une adresse que le serveur peut utiliser pour se connecter au client, comme indiqué dans l’exemple de configuration suivant.

Remarque

Les clients non duplex qui ne parviennent pas à s'authentifier à l'aide d'une conversation sécurisée lèvent en général une exception MessageSecurityException. Toutefois, si un client duplex qui utilise une conversation sécurisée ne parvient pas à s’authentifier, il reçoit un TimeoutException à la place.

Si vous créez un client/service à l’aide de l’élément WSHttpBinding et que vous n’incluez pas le point de terminaison de rappel du client, vous recevrez l’erreur suivante.

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

L’exemple de code suivant montre comment spécifier l’adresse du point de terminaison client par programmation.

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

L’exemple de code suivant montre comment spécifier l’adresse du point de terminaison client dans la configuration.

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

Avertissement

Le modèle duplex ne détecte pas automatiquement lorsqu’un service ou un client ferme son canal. Par conséquent, si un client se termine de façon inattendue, par défaut, le service ne sera pas averti ou si un service se termine de façon inattendue, le client ne sera pas averti. Si vous utilisez un service déconnecté, l’exception CommunicationException est levée. Les clients et les services peuvent implémenter leur propre protocole pour les avertir les uns des autres s’ils le choisissent. Pour plus d’informations sur la gestion des erreurs, consultez gestion des erreurs WCF.

Voir aussi