Dienst-Kanalebenenprogrammierung
In diesem Thema wird das Schreiben einer Dienstanwendung ohne Verwenden der Windows Communication Foundation (WCF) und der System.ServiceModel.ServiceHost zugehörigen Objektmodelle beschrieben.
Empfangen von Nachrichten
Um bereit zu sein, Meldungen zu empfangen und zu verarbeiten, sind die folgenden Schritte erforderlich:
Erstellen Sie eine Bindung.
Erstellen Sie einen Kanallistener.
Öffnen Sie den Kanallistener.
Lesen Sie die Anforderung, und senden Sie eine Antwort.
Schließen Sie alle Kanalobjekte.
Erstellen einer Bindung
Der erste Schritt beim Lauschen und Empfangen von Meldungen ist das Erstellen einer Bindung. WCF wird mit verschiedenen integrierten und vom System bereitgestellten Bindungen ausgeliefert, die direkt durch Instanziierung verwendet werden können. Außerdem können Sie eigene benutzderdefinierte Bindungen durch Instanziieren einer CustomBinding-Klasse erstellen. Diese Aufgabe übernimmt beispielsweise der Code im Programmbeispiel 1.
Das Codebeispiel unten erstellt eine Instanz von System.ServiceModel.Channels.CustomBinding und fügt ein System.ServiceModel.Channels.HttpTransportBindingElement zur Elementesammlung hinzu, die einer Sammlung von Bindungselementen entspricht, die zum Erstellen des Kanalstapels verwendet werden. Da die Elementesammlung in diesem Beispiel nur das HttpTransportBindingElement aufweist, hat der resultierende Kanalstapel nur einen HTTP-Transportkanal.
Erstellen eines Kanallisteners.
Nach dem Erstellen einer Bindung wird Binding.BuildChannelListener aufgerufen, um den Kanallistener zu erstellen, wobei der Typparameter der zu erstellenden Kanalform entspricht. In diesem Beispiel wird System.ServiceModel.Channels.IReplyChannel verwendet, da eingehende Meldungen in einem Anforderungs-/Antwort-Meldungsaustauschmuster abgehört werden sollen.
IReplyChannel wird zum Empfangen von Anforderungsmeldungen und Senden von Antwortnachrichten verwendet. Durch das Aufrufen von IReplyChannel.ReceiveRequest wird ein System.ServiceModel.Channels.IRequestChannel zurückgegeben, der zum Empfang der Anforderungsmeldung und zum Senden einer Antwortmeldung verwendet werden kann.
Beim Erstellen des Listeners wird die Netzwerkadresse übergeben, die dieser abhört, in diesem Fall http://localhost:8080/channelapp
. Allgemein unterstützt jeder Transportkanal mindestens ein Adressenschema. So unterstützt beispielsweise der HTTP-Transport HTTP- und HTTPS-Schemas.
Außerdem wird beim Erstellen des Listeners eine leere System.ServiceModel.Channels.BindingParameterCollection übergeben. Ein Bindungsparameter ist ein Mechanismus, um Parameter zu übergeben, die steuern, wie der Listener erstellt werden soll. In diesem Beispiel werden keine solchen Parameter verwendet, daher wird eine leere Auflistung übergeben.
Abhören von eingehenden Nachrichten
Dann wird ICommunicationObject.Open auf dem Listener aufgerufen und damit begonnen, Kanäle zu akzeptieren. Das Verhalten von IChannelListener<TChannel>.AcceptChannel hängt davon ab, ob der Transport verbindungsorientiert oder verbindungslos ist. Bei einem verbindungsorientierten Transport blockiert AcceptChannel, bis eine neue Verbindungsanforderung eingeht. Dann wird ein neuer Kanal zurückgegeben, der diese neue Verbindung darstellt. Bei einem verbindungslosen Transport, wie z. B. HTTP, gibt AcceptChannel unverzüglich den einzigen Kanal zurück, den der Transportlistener erstellt.
In diesem Beispiel gibt der Listener einen Kanal zurück, der IReplyChannel implementiert. Um Meldungen auf diesem Kanal zu empfangen, wird zuerst ICommunicationObject.Open aufgerufen, um ihn in einen kommunikationsbereiten Status zu versetzen. Dann wird ReceiveRequest aufgerufen, 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. Die Aktion und der Textinhalt der Meldung (es wird angenommen, dass es sich um eine Zeichenfolge handelt) wird geschrieben.
Zum Senden einer Antwort wird eine neue Antwortmeldung erstellt, in diesem Fall durch Zurücksenden der Zeichenfolgendaten, die in der Anforderung eingegangen sind. Dann wird Reply aufgerufen, um die Antwortmeldung zu senden.
Schließen von Objekten
Um Ressourcenverluste zu vermeiden, sollten Sie in Kommunikationsvorgängen verwendete Objekte schließen, wenn sie nicht mehr benötigt werden. In diesem Beispiel werden die Anforderungsmeldung, der Anforderungskontext, der Kanal und der Listener geschlossen.
Im folgenden Codebeispiel wird ein grundlegender Dienst, in dem ein Kanallistener nur eine Nachricht empfängt, veranschaulicht. Ein wirklicher Dienst akzeptiert Kanäle und empfängt Meldungen, 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: {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