Freigeben über


Dienst-Kanalebenenprogrammierung

In diesem Thema wird beschrieben, wie Sie eine WCF-Dienstanwendung (Windows Communication Foundation) ohne die Verwendung des System.ServiceModel.ServiceHost und seines zugehörigen Objektmodells schreiben.

Empfangen von Nachrichten

Um bereit zum Empfangen und Verarbeiten von Nachrichten zu sein, sind die folgenden Schritte erforderlich:

  1. Erstellen Sie eine Bindung.

  2. Erstellen Sie einen Kanallistener.

  3. Öffnen Sie den Kanallistener.

  4. Lesen Sie die Anforderung, und senden Sie eine Antwort.

  5. Schließen Sie alle Kanalobjekte.

Erstellen einer Bindung

Der erste Schritt beim Überwachen und Empfangen von Nachrichten besteht darin, eine Bindung zu erstellen. WCF wird mit mehreren integrierten oder vom System bereitgestellten Bindungen ausgeliefert, die direkt verwendet werden können, indem eine instanziiert wird. Darüber hinaus können Sie auch eine eigene benutzerdefinierte Bindung erstellen, indem Sie eine CustomBinding-Klasse instanziieren, die den Code in Eintrag 1 ausführt.

Im folgenden Codebeispiel wird eine Instanz von System.ServiceModel.Channels.CustomBinding erstellt und System.ServiceModel.Channels.HttpTransportBindingElement zur Elements-Auflistung hinzugefügt. Diese Auflistung ist eine Sammlung von Bindungselementen, die zum Erstellen des Kanalstapels verwendet werden. In diesem Beispiel enthält die Elemente-Sammlung nur das HttpTransportBindingElement, daher besteht der resultierende Kanalstapel nur aus dem HTTP-Transportkanal.

Erstellen eines ChannelListeners

Nach dem Erstellen einer Bindung rufen wir Binding.BuildChannelListener auf, um den Kanallistener zu erstellen, wobei der Typparameter die Form des Kanals angibt, die erstellt werden soll. In diesem Beispiel verwenden System.ServiceModel.Channels.IReplyChannel wir, weil wir eingehende Nachrichten in einem Anforderungs-/Antwortnachrichtenaustauschmuster überwachen möchten.

IReplyChannel wird zum Empfangen von Anforderungsnachrichten und zum Senden von Zurückantwortnachrichten verwendet. Das Aufrufen von IReplyChannel.ReceiveRequest erzeugt ein System.ServiceModel.Channels.IRequestChannel, das zum Empfangen der Anforderungsnachricht und zur Rücksendung einer Antwortnachricht verwendet werden kann.

Beim Erstellen des Listeners wird die Netzwerkadresse übergeben, die dieser abhört, in diesem Fall http://localhost:8080/channelapp. Im Allgemeinen unterstützt jeder Transportkanal ein oder mehrere Adressschemas. Zum Beispiel unterstützt der HTTP-Transport sowohl die HTTP- als auch die HTTPS-Schemas.

Außerdem geben wir beim Erstellen des Listeners ein leeres System.ServiceModel.Channels.BindingParameterCollection weiter. Ein Bindungsparameter ist ein Mechanismus zum Übergeben von Parametern, die steuern, wie der Listener erstellt werden soll. In unserem Beispiel verwenden wir keine solchen Parameter, sodass wir eine leere Auflistung übergeben.

Überwachung eingehender Nachrichten

Dann wird ICommunicationObject.Open auf dem Listener aufgerufen und damit begonnen, Kanäle zu akzeptieren. Das Verhalten IChannelListener<TChannel>.AcceptChannel hängt davon ab, ob der Transport verbindungsorientiert oder verbindungsfrei ist. Bei verbindungsorientierten Transporten blockiert AcceptChannel bis eine neue Verbindungsanforderung eingeht; dann wird ein neuer Kanal zurückgegeben, der die neue Verbindung darstellt. Bei verbindungslosen Transporten, wie HTTP, gibt AcceptChannel sofort den einzigen Kanal zurück, den der Transportlistener erstellt.

In diesem Beispiel gibt der Listener einen Kanal zurück, der IReplyChannel implementiert. Um Nachrichten in diesem Kanal zu empfangen, rufen wir zuerst ICommunicationObject.Open auf, um ihn in einen Zustand zu versetzen, der für die Kommunikation bereit ist. Wir rufen dann ReceiveRequest auf, der blockiert, bis eine Nachricht eingeht.

Lesen der Anforderung und Senden einer Antwort

Wenn ReceiveRequest einen RequestContext zurückgibt, geht die empfangene Meldung mithilfe der RequestMessage-Eigenschaft ein. Wir schreiben die Aktion und den Textkörper der Nachricht aus (dies wird davon ausgegangen, dass es sich um eine Zeichenfolge handelt).

Zum Senden einer Antwort erstellen wir in diesem Fall eine neue Antwortnachricht, die die in der Anforderung empfangenen Zeichenfolgendaten zurückgibt. Anschließend rufen Reply wir auf, um die Antwortnachricht zu senden.

Schließen von Objekten

Um ressourcenlecks zu vermeiden, ist es sehr wichtig, Objekte zu schließen, die in der Kommunikation verwendet werden, wenn sie nicht mehr benötigt werden. In diesem Beispiel schließen wir die Anforderungsnachricht, den Anforderungskontext, den Kanal und den Listener.

Das folgende Codebeispiel zeigt einen grundlegenden Dienst, in dem ein Kanallistener nur eine Nachricht empfängt. Ein echter Dienst akzeptiert weiterhin Kanäle und empfängt Nachrichten, bis der Dienst beendet wird.

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: {message.Headers.Action}");
    string data=message.GetBody<string>();
    Console.WriteLine($"Message content: {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