Programmation de service au niveau du canalService Channel-Level Programming

Cette rubrique décrit comment écrire une application de service Windows Communication Foundation (WCF) sans utiliser le System.ServiceModel.ServiceHost et son modèle objet associé.This topic describes how to write a Windows Communication Foundation (WCF) service application without using the System.ServiceModel.ServiceHost and its associated object model.

Réception des messagesReceiving Messages

Pour pouvoir recevoir et traiter des messages, vous devez effectuer les étapes suivantes :To be ready to receive and process messages, the following steps are required:

  1. Créer une liaison.Create a binding.

  2. Générer un écouteur de canal.Build a channel listener.

  3. Ouvrir l'écouteur de canal.Open the channel listener.

  4. Lire la demande et envoyer une réponse.Read the request and send a reply.

  5. Fermer tous les objets de canal.Close all channel objects.

Création d’une liaisonCreating a Binding

La première étape pour écouter et recevoir des messages consiste à créer une liaison.The first step in listening for and receiving messages is creating a binding. WCF est fourni avec plusieurs liaisons intégrées ou fournies par le système qui peuvent être utilisées directement par l’instanciation d’un d’eux.WCF ships with several built-in or system-provided bindings that can be used directly by instantiating one of them. Vous pouvez également créer votre propre liaison personnalisée en instanciant une classe CustomBinding, comme dans le code n°1.In addition, you can also create your own custom binding by instantiating a CustomBinding class which is what the code in listing 1 does.

L’exemple de code suivant crée une instance de System.ServiceModel.Channels.CustomBinding et ajoute un System.ServiceModel.Channels.HttpTransportBindingElement à sa collection Elements qui est une collection d’éléments de liaison utilisés pour générer la pile de canaux.The code example below creates an instance of System.ServiceModel.Channels.CustomBinding and adds an System.ServiceModel.Channels.HttpTransportBindingElement to its Elements collection which is a collection of binding elements that are used to build the channel stack. Dans cet exemple, la collection d'éléments ayant uniquement le HttpTransportBindingElement, la pile de canaux résultante dispose uniquement du canal de transport HTTP.In this example, because the elements collection has only the HttpTransportBindingElement, the resulting channel stack has only the HTTP transport channel.

Génération d'un ChannelListenerBuilding a ChannelListener

Après avoir créé une liaison, nous appelons Binding.BuildChannelListener pour générer l'écouteur de canal où le paramètre de type est la forme de canal à créer.After creating a binding, we call Binding.BuildChannelListener to build the channel listener where the type parameter is the channel shape to create. Dans cet exemple, nous utilisons System.ServiceModel.Channels.IReplyChannel car nous souhaitons écouter les messages entrants dans un modèle d'échange de messages demande/réponse.In this example we are using System.ServiceModel.Channels.IReplyChannel because we want to listen for incoming messages in a request/reply message exchange pattern.

IReplyChannel est utilisé pour recevoir des messages de demande et renvoyer des messages de réponse.IReplyChannel is used for receiving request messages and sending back reply messages. IReplyChannel.ReceiveRequest retourne un System.ServiceModel.Channels.IRequestChannel, qui peut être utilisé pour recevoir le message de demande et renvoyer un message de réponse.Calling IReplyChannel.ReceiveRequest returns an System.ServiceModel.Channels.IRequestChannel, which can be used to receive the request message and to send back a reply message.

Lors de la création de l'écouteur, nous passons l'adresse réseau sur laquelle il écoute, en l'occurrence http://localhost:8080/channelapp.When creating the listener, we pass the network address on which it listens, in this case http://localhost:8080/channelapp. En général, chaque canal de transport prend en charge un ou plusieurs schémas d'adressage ; le transport HTTP, par exemple, prend en charge les schémas http et https.In general, each transport channel supports one or possibly several address schemes, for example, the HTTP transport supports both http and https schemes.

Nous passons également un System.ServiceModel.Channels.BindingParameterCollection vide lors de la création de l'écouteur.We also pass an empty System.ServiceModel.Channels.BindingParameterCollection when creating the listener. Un paramètre de liaison est un mécanisme permettant de passer des paramètres qui contrôlent la manière dont l’écouteur doit être généré.A binding parameter is a mechanism to pass parameters that control how the listener should be built. Dans notre exemple, nous n’utilisons pas de tels paramètres ; nous passons donc une collection vide.In our example, we are not using any such parameters so we pass an empty collection.

Écoute des messages entrantsListening for Incoming Messages

Nous appelons ensuite ICommunicationObject.Open sur l'écouteur et nous commençons à accepter des canaux.We then call ICommunicationObject.Open on the listener and start accepting channels. Le comportement de IChannelListener<TChannel>.AcceptChannel varie selon que le transport est orienté connexion ou sans connexion.The behavior of IChannelListener<TChannel>.AcceptChannel depends on whether the transport is connection-oriented or connection-less. Pour les transports orientés connexion, AcceptChannel se bloque jusqu'à ce qu'une nouvelle demande de connexion arrive, auquel point il retourne un nouveau canal qui représente cette nouvelle connexion.For connection-oriented transports, AcceptChannel blocks until a new connection request comes in at which point it returns a new channel that represents that new connection. Pour les transports sans connexion, tels que HTTP, AcceptChannel est retourné immédiatement avec l'unique canal que l'écouteur de transport crée.For connection-less transports, such as HTTP, AcceptChannel returns immediately with the one and only channel that the transport listener creates.

Dans cet exemple, l'écouteur retourne un canal qui implémente IReplyChannel.In this example, the listener returns a channel that implements IReplyChannel. Pour recevoir des messages sur ce canal, nous appelons d'abord ICommunicationObject.Open sur lui afin de le placer dans un état prêt pour la communication.To receive messages on this channel we first call ICommunicationObject.Open on it to place it in a state ready for communication. Nous appelons ensuite ReceiveRequest, qui se bloque jusqu'à ce qu'un message arrive.We then call ReceiveRequest which blocks until a message arrives.

Lecture de la demande et envoi de la réponseReading the Request and Sending a Reply

Lorsque ReceiveRequest retourne un RequestContext, nous obtenons le message reçu à l'aide de sa propriété RequestMessage.When ReceiveRequest returns a RequestContext, we get the received message using its RequestMessage property. Nous écrivons l'action et le contenu du corps du message (que nous supposons être une chaîne).We write out the message’s action and body content, (which we assume is a string).

Pour envoyer une réponse, nous créons un message de réponse ; en l'occurrence, nous repassons les données de chaîne que nous avons reçues dans la demande.To send a reply, we create a new reply message in this case passing back the string data we received in the request. Nous appelons ensuite Reply pour envoyer le message de réponse.We then call Reply to send the reply message.

Fermeture des objetsClosing Objects

Pour éviter toute fuite des ressources, il est très important de fermer les objets utilisés dans les communications lorsqu'ils ne sont plus nécessaires.To avoid leaking resources, it is very important to close objects used in communications when they are no longer required. Dans cet exemple, nous fermons le message de demande, le contexte de demande, le canal et l'écouteur.In this example we close the request message, the request context, the channel and the listener.

L'exemple de code suivant illustre un service de base dans lequel un écouteur de canal reçoit un seul message.The following code example shows a basic service in which a channel listener receives only one message. Un vrai service continue à accepter des canaux et à recevoir des messages jusqu'à ce que le service s'arrête.A real service keeps accepting channels and receiving messages until the service exits.

using System;
using System.ServiceModel.Channels;
namespace ProgrammingChannels
{
class Service
{
static void RunService()
{
    //Step1: Create a custom binding with just TCP.
    BindingElement[] bindingElements = new BindingElement[2];
    bindingElements[0] = new TextMessageEncodingBindingElement();
    bindingElements[1] = new HttpTransportBindingElement();

    CustomBinding binding = new CustomBinding(bindingElements);


    //Step2: Use the binding to build the channel listener.         
    IChannelListener<IReplyChannel> listener =
          binding.BuildChannelListener<IReplyChannel>(
             new Uri("http://localhost:8080/channelapp"),
           new BindingParameterCollection());

    //Step3: Listening for messages.
    listener.Open();
    Console.WriteLine(
           "Listening for incoming channel connections");
    //Wait for and accept incoming connections.
    IReplyChannel channel = listener.AcceptChannel();
    Console.WriteLine("Channel accepted. Listening for messages");
    //Open the accepted channel.
    channel.Open();
    //Wait for and receive a message from the channel.
    RequestContext request= channel.ReceiveRequest();
    //Step4: Reading the request message.
    Message message = request.RequestMessage;
    Console.WriteLine("Message received");
    Console.WriteLine("Message action: {0}",
                          message.Headers.Action);
    string data=message.GetBody<string>();
    Console.WriteLine("Message content: {0}",data);
    //Send a reply.
    Message replymessage=Message.CreateMessage(
        binding.MessageVersion, 
        "http://contoso.com/someotheraction", 
         data);
    request.Reply(replymessage);
    //Step5: Closing objects.
    //Do not forget to close the message.
    message.Close();
    //Do not forget to close RequestContext.
    request.Close();
    //Do not forget to close channels.
    channel.Close();
    //Do not forget to close listeners.
    listener.Close();
}
public static void Main()
{
    Service.RunService();
    Console.WriteLine("Press enter to exit");
    Console.ReadLine();
}
}
}
Imports System.ServiceModel.Channels

Namespace ProgrammingChannels
    Friend Class Service

        Private Shared Sub RunService()

            'Step1: Create a custom binding with just TCP.
            Dim bindingElements(1) As BindingElement = {New TextMessageEncodingBindingElement(), _
                                                        New HttpTransportBindingElement()}

            Dim binding As New CustomBinding(bindingElements)

            'Step2: Use the binding to build the channel listener.         
            Dim listener = binding.BuildChannelListener(Of IReplyChannel)(New Uri("http://localhost:8080/channelapp"), _
                                                                          New BindingParameterCollection())

            'Step3: Listening for messages.
            listener.Open()
            Console.WriteLine("Listening for incoming channel connections")

            'Wait for and accept incoming connections.
            Dim channel = listener.AcceptChannel()
            Console.WriteLine("Channel accepted. Listening for messages")

            'Open the accepted channel.
            channel.Open()

            'Wait for and receive a message from the channel.
            Dim request = channel.ReceiveRequest()

            'Step4: Reading the request message.
            Dim message = request.RequestMessage
            Console.WriteLine("Message received")
            Console.WriteLine("Message action: {0}", message.Headers.Action)
            Dim data = message.GetBody(Of String)()
            Console.WriteLine("Message content: {0}", data)
            'Send a reply.
            Dim replymessage = Message.CreateMessage(binding.MessageVersion, _
                                                     "http://contoso.com/someotheraction", data)
            request.Reply(replymessage)
            'Step5: Closing objects.
            'Do not forget to close the message.
            message.Close()
            'Do not forget to close RequestContext.
            request.Close()
            'Do not forget to close channels.
            channel.Close()
            'Do not forget to close listeners.
            listener.Close()
        End Sub

        Public Shared Sub Main()

            Service.RunService()
            Console.WriteLine("Press enter to exit")
            Console.ReadLine()

        End Sub

    End Class
End Namespace