Empfänger und Empfängerketten
Channels senden jede Nachricht vor dem Senden oder nach dem Empfang durch eine Kette von Channelempfängerobjekten. Diese Empfängerkette enthält Empfänger, die für die grundlegenden Channelfunktionen erforderlich sind, z. B. Formatierungsempfänger, Transportempfänger oder Empfänger, die Stapel erstellen. Sie können die Channelempfängerkette aber auch für die Ausführung spezieller Aufgaben im Zusammenhang mit einer Nachricht oder einem Stream anpassen.
Jeder Channelempfänger implementiert entweder IClientChannelSink oder IServerChannelSink. Der erste Channelempfänger auf der Clientseite muss außerdem IMessageSink implementieren. Er implementiert in der Regel IClientFormatterSink (erbt von IMessageSink, IChannelSinkBase und IClientChannelSink) und wird als Formatierungsempfänger bezeichnet, da er die eingehende Nachricht in einen Stream (ein IMessage-Objekt) transformiert.
Die Channelempfängerkette verarbeitet jede Nachricht, die an eine oder von einer Anwendungsdomäne gesendet wird. Zu diesem Zeitpunkt liegt lediglich die Nachricht, Sie können diese aber wie gewünscht verarbeiten. Bei jeder weiteren Verarbeitung wird dann die Nachricht verwendet, die Sie verarbeitet und an das System zurückgegeben haben. Dies ist ein geeigneter Punkt, um einen Protokollierdienst, Filter jeder Art oder auch Verschlüsselung oder andere Sicherheitsmaßnahmen auf dem Client oder Server zu implementieren. In der folgenden Abbildung ist die Struktur einer einfachen Channelempfängerkette dargestellt.
Einfache Channelempfängerkette
Beachten Sie, dass jeder Channelempfänger den Stream verarbeitet und ihn dann an den nächsten Channelempfänger übergibt. Das bedeutet, dass Objekte, die in der Kette vor oder nach dem Empfänger stehen, wissen müssen, wie sie mit dem an sie übergebenen Stream verfahren sollen.
Hinweis
Nachrichtenempfänger dürfen keine Ausnahmen auslösen. Eine Möglichkeit, wie Nachrichtenempfänger dies steuern können, besteht darin, Methodencode in try-catch-Blöcke einzuschließen.
Channelempfängeranbieter (Objekte, die die IClientChannelSinkProvider-Schnittstelle, die IClientFormatterSinkProvider-Schnittstelle oder die IServerChannelSinkProvider-Schnittstelle implementieren) sind für die Erstellung der Channelempfänger zuständig, die Remotingnachrichten durchlaufen. Wenn ein Remotetyp aktiviert wird, wird der Channelempfängeranbieter aus dem Channel abgerufen. Um die erste Channelempfänger aus der Kette abzurufen, wird die CreateSink-Methode für den Empfängeranbieter aufgerufen.
Channelempfänger sind für die Nachrichtenübermittlung zwischen Client und Server zuständig. Channelempfänger sind außerdem in einer Kette miteinander verknüpft. Wenn die CreateSink-Methode für einen Empfängeranbieter aufgerufen wird, muss dieser Folgendes ausführen:
Erstellen eines eigenen Channelempfängers.
Aufrufen von CreateSink für den nächsten Empfängeranbieter in der Kette.
Sicherstellen, dass der nächste und der aktuelle Empfänger miteinander verknüpft sind.
Zurückgeben des Empfängers an den Aufrufer.
Channelempfänger haben die Aufgabe, alle Aufrufe, die für sie ausgegeben werden, an den nächsten Empfänger in der Kette weiterzuleiten, und sollten ein Verfahren für das Speichern eines Verweises auf dem nächsten Empfänger bereitstellen.
Channelempfänger sind sehr flexibel bei der Festlegung dessen, was in der Empfängerkette weitergeleitet wird. So können z. B. Sicherheitsempfänger, die vor dem Senden der serialisierten ursprünglichen Nachricht die Authentifizierung aushandeln möchten, die vollständige Channelnachricht zurückhalten, den Inhaltsstream durch eigenen Inhalt ersetzen und ihn dann durch die Empfängerkette zur Remoteanwendungsdomäne senden. Bei der Rückübertragung kann der Sicherheitsempfänger die Antwortnachricht abfangen und eine Verbindung mit den entsprechenden Sicherheitsempfängern in der Remoteanwendungsdomäne erstellen. Sobald eine Einigung erzielt wurde, kann der ursprüngliche Sicherheitsempfänger den ursprünglichen Inhaltsstream an die Remoteanwendungsdomäne senden.
Nachrichtenverarbeitung in der Channelempfängerkette
Sobald das .NET Remoting-System einen Channel findet, der die IMethodCallMessage-Implementierung verarbeiten kann, übergibt dieser Channel die Nachricht an den Formatierungschannelempfänger, indem er IMessageSink.SyncProcessMessage (bzw. IMessageSink.AsyncProcessMessage) aufruft. Der Formatierungsempfänger erstellt das Transportheaderarray und ruft IClientChannelSink.GetRequestStream für den nächsten Empfänger auf. Dieser Aufruf wird durch die Empfängerkette weitergeleitet, und jeder Empfänger kann einen Anforderungsstream erstellen, der an den Formatierungsempfänger zurückgegeben wird. Wenn GetRequestStream einen NULL-Verweis (Nothing in Visual Basic) zurückgibt, erstellt der Formatierungsempfänger eines eigenen Empfängers für die Serialisierung. Sobald dieser Aufruf beendet ist, wird die Nachricht serialisiert und die entsprechende Nachrichtenverarbeitungsmethode für den ersten Channelempfänger in der Empfängerkette aufgerufen.
Empfänger können keine Daten in den Stream schreiben, aber Daten aus dem Stream lesen oder bei Bedarf einen neuen Stream weitergeben. Außerdem können Empfänger dem Headerarray Header hinzufügen (wenn sie nicht zuvor GetRequestStream für den nächsten Empfänger aufgerufen haben) und sich selbst vor dem Weiterleiten des Aufrufs an den nächsten Empfänger dem Empfängerstapel hinzufügen. Wenn der Aufruf des Transportempfängers am Ende der Kette erreicht, sendet der Transportempfänger die Header und die serialisierte Nachricht über den Channel an den Server, wo der gesamte Prozess umgekehrt abläuft. Der Transportempfänger (auf der Serverseite) ruft die Header und die serialisierte Nachricht von der Serverseite des Streams ab und leitet diese durch die Empfängerkette bis zum Formatierungsempfänger weiter. Der Formatierungsempfänger deserialisiert die Nachricht und leitet sie an das Remotingsystem weiter, wo sie wieder in einen Methodenaufruf umgewandelt und für das Serverobjekt aufgerufen wird.
Erstellen von Channelempfängerketten
Zum Erstellen eines neuen Channelempfängers müssen Sie das Remotingsystem so implementieren und konfigurieren, dass es eine IServerChannelSinkProvider-Implementierung oder eine IClientChannelSinkProvider-Implementierung erkennt, die die benutzerdefinierte IClientChannelSink-Implementierung oder IServerChannelSink-Implementierung erstellen oder den nächsten Empfänger in der Kette abrufen kann. Sie können die abstrakte BaseChannelSinkWithProperties-Klasse zur Unterstützung bei der Implementierung der benutzerdefinierten Channelempfänger verwenden.
Erstellen eines Channelempfängeranbieters
Anwendungen können beim Erstellen eines Channels einen Channelempfängeranbieter für den Client oder Server als Parameter bereitstellen. Channelempfängeranbieter sollten in einer Kette gespeichert werden, und es ist die Aufgabe des Benutzers, alle Channelempfängeranbieter zu verketten, bevor der äußere an den Channelkonstruktor übergeben wird. Der Channelempfängeranbieter implementiert zu diesem Zweck eine Next-Eigenschaft. Im folgenden Codebeispiel wird die Erstellung eines Channelempfängeranbieters auf der Clientseite veranschaulicht. Ein vollständiges Beispiel finden Sie unter Remotingbeispiel: Channelempfängeranbieter.
private Function CreateDefaultClientProviderChain() As IClientChannelSinkProvider
Dim chain As New FirstClientFormatterSinkProvider
Dim sink As IClientChannelSinkProvider
sink = chain
sink.Next = New SecondClientFormatterSinkProvider
sink = sink.Next
return chain
End Function
private IClientChannelSinkProvider CreateDefaultClientProviderChain(){
IClientChannelSinkProvider chain = new FirstClientFormatterSinkProvider();
IClientChannelSinkProvider sink = chain;
sink.Next = new SecondClientFormatterSinkProvider();
sink = sink.Next;
return chain;
}
Hinweis
Wenn in einer Konfigurationsdatei mehrere Channelempfängeranbieter angegeben sind, werden diese vom Remotingsystem in der Reihenfolge verkettet, in der sie in der Konfigurationsdatei gefunden werden. Die Channelempfängeranbieter werden erstellt, wenn der Channel während des Aufrufs von RemotingConfiguration.Configure erstellt wird.
Formatierungsempfänger
Formatierungsempfänger serialisieren die Channelnachricht als Objekt, das IMessage implementiert, in den Nachrichtenstream. Einige Implementierungen von Formatierungsempfängern verwenden die vom System bereitgestellten Typen von Formatierungsprogrammen (BinaryFormatter und SoapFormatter). Andere Implementierungen verfügen über eigene Möglichkeiten, um die Channelnachricht in den Stream zu transformieren.
Die Funktion des Formatierungsempfängers ist es, die erforderlichen Header zu generieren und die Nachricht in den Stream zu serialisieren. Nach dem Formatierungsempfänger wird die Nachricht durch den Aufruf von IMessageSink.ProcessMessage oder Imessagesink.AsyncProcessMessage an alle Empfänger in der Empfängerkette weitergeleitet. Zu diesem Zeitpunkt ist die Nachricht bereits serialisiert und wird lediglich zu Informationszwecken bereitgestellt.
Hinweis
Empfänger, die die Nachricht erstellen oder ändern müssen, müssen in der Empfängerkette vor dem Formatierungsprogramm platziert werden. Dies lässt sich problemlos durch die Implementierung von IClientFormatterSink erreichen, wodurch dem System vorgetäuscht wird, es verfüge über einen Verweis auf den Formatierungsempfänger. Der echte Formatierungsempfänger kann dann später in der Empfängerkette platziert werden.
Bei der Rückübertragung transformiert den Formatierungsempfänger den Nachrichtenstream wieder in die Elemente der Channelnachricht (Rückgabenachricht). Der erste Empfänger auf der Clientseite muss die IClientFormatterSink-Schnittstelle implementieren. Wenn CreateSink an den Channel zurückgegeben wird, wird der zurückgegebene Verweis in einen IClientFormatterSink-Typ umgewandelt, sodass SyncProcessMessage der IMessage-Schnittstelle aufgerufen werden kann. Falls die Umwandlung fehlschlägt, löst das System eine Ausnahme aus.
Benutzerdefinierte Channelempfänger
Auf der Clientseite werden benutzerdefinierte Channelempfänger in die Kette von Objekten zwischen dem Formatierungsempfänger und dem letzten Transportempfänger eingefügt. Durch das Einfügen eines benutzerdefinierten Channelempfängers in den Client- oder Serverchannel kann IMessage an einem von zwei Punkten verarbeitet werden:
Während des Prozesses, durch den ein als Nachricht dargestellter Aufruf in einen Stream konvertiert und über die Verbindung gesendet wird.
Während des Prozesses, durch den ein Stream aus der Verbindung entnommen und an das StackBuilderSink-Objekt (der letzte Nachrichtenempfänger vor dem Remoteobjekt auf dem Server) bzw. das Proxyobjekt (auf dem Client) gesendet wird.
Benutzerdefinierte Empfänger können Daten aus dem Stream lesen oder in den Stream schreiben (je nachdem, ob es sich um einen ein- oder ausgehenden Aufruf handelt) und ggf. den Headern zusätzliche Informationen hinzufügen. Zu diesem Zeitpunkt ist die Nachricht bereits durch das Formatierungsprogramm serialisiert worden und kann nicht geändert werden. Wenn der Nachrichtenaufruf an den Transportempfänger am Ende der Kette weitergeleitet wird, schreibt der Transportempfänger die Header in den Stream und leitet den Stream unter Verwendung des vom Channel vorgegebenen Transportprotokolls an den Transportempfänger auf dem Server weiter.
Transportempfänger
Der Transportempfänger ist der letzte Empfänger in der Kette auf der Clientseite und der erste Empfänger in der Kette auf der Serverseite. Neben der Übermittlung der serialisierten Nachricht hat der Transportempfänger auch die Aufgabe, die Header an den Server zu senden und die Header sowie den Stream bei Rückgabe des Aufrufs vom Server abzurufen. Diese Empfänger sind in den Channel integriert und können nicht erweitert werden.
Ersetzen des Standardformatierungsprogramms
Da es sich bei einem Channel um einen abstrakten Netzwerkmechanismus handelt, können Sie das .NET Remoting-System so konfigurieren, dass ein vom System implementierter Channel mit einem beliebigen Formatierungsprogramm kombiniert wird. Sie können dazu den Channelkonstruktor verwenden, der eine IDictionary-Implementierung der Channeleigenschaften, ein serverseitiges Formatierungsprogramm und ein clientseitiges Formatierungsprogramm akzeptiert. Sie können das Formatierungsprogramm auch in einer Konfigurationsdatei angeben. Im folgenden Beispiel wird das .NET Remoting-Konfigurationssystem angewiesen, einen HttpChannel zu erstellen, jedoch auf der Clientseite BinaryClientFormatterSink zu verwenden.
<configuration>
<system.runtime.remoting>
<application>
<channels>
<channel ref="http">
<clientProviders>
<formatter ref="binary"/>
</clientProviders>
<channels>
</application>
</system.runtime.remoting>
</configuration>
Im folgenden Code wird dieselbe Aufgabe programmgesteuert ausgeführt, wobei der Remoteschnittstellentyp IService angenommen wird, der GetServerString und GetServerTime implementiert.
Imports System
Imports System.Collections
Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Channels
Imports System.Runtime.Remoting.Channels.Http
Public Class ClientProcess
<MTAThread()> _
Public Shared Sub Main()
' Note that any name/value pairs of configuration attributes can be
' placed in this dictionary (the configuration system calls this same
' constructor).
Dim properties As New Hashtable()
properties("name") = "HttpBinary"
ChannelServices.RegisterChannel(New HttpChannel(properties, New BinaryClientFormatterSinkProvider(), Nothing))
' The last parameter above (Nothing) is the server sink provider chain
' to obtain the default behavior (which includes SOAP and
' binary formatters on the server side).
Dim service As IService = CType(Activator.GetObject(GetType(IService), "http://computer:8080/SAService"), IService)
Console.WriteLine("Server string is: " + service.GetServerString())
Console.WriteLine("Server time is: " + service.GetServerTime())
End Sub
End Class
using System;
using System.Collections;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
public class ClientProcess{
public static void Main(string[] Args){
// Note that any name/value pairs of configuration attributes can be
// placed in this dictionary (the configuration system calls this
// same HttpChannel constructor).
IDictionary properties = new Hashtable();
properties["name"] = "HttpBinary";
// The last parameter below is the server sink provider chain
// to obtain the default behavior (which includes SOAP and binary
// formatters) on the server side.
ChannelServices.RegisterChannel(new HttpChannel(null, new BinaryClientFormatterSinkProvider(), null));
IService service = (IService)Activator.GetObject(typeof(IService),"http://computer:8080/SAService");
Console.WriteLine("Server string is: " + service.GetServerString());
Console.WriteLine("Server time is: " + service.GetServerTime());
}
}
Ein vollständiges Beispiel für diese in Internetinformationsdienste (Internet Information Services, IIS) gehostete Kombination aus Channel und Formatierungsprogramm finden Sie unter Remotingbeispiel: Hosten in Internetinformationsdienste.
Wenn dieser Client so geändert werden soll, dass ein TcpChannel-Objekt mit dem SoapClientFormatterSink-Objekt verwendet wird, müssen Sie nur die Namespaces und den Aufruf von RegisterChannel ändern, wie im folgenden Codebeispiel veranschaulicht.
ChannelServices.RegisterChannel(New TcpChannel(properties, New SoapClientFormatterSinkProvider(), Nothing))
ChannelServices.RegisterChannel(new TcpChannel(null, new SoapClientFormatterSinkProvider(), null));
Siehe auch
Konzepte
Hosten von Remoteobjekten in Internetinformationsdienste
Remotingbeispiel: Hosten in Internetinformationsdienste