Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
In diesem Codebeispiel wird ein ChannelSinkPropertySetterProvider erstellt, der ein Channelempfängerprovider ist, der Werte in der Anwendungskonfigurationsdatei lesen und den Empfängerstapel nach Channelempfängern durchsuchen kann, die die in dieser Datei enthaltenen Eigenschaften unterstützen. Dies kann nützlich sein, um eine Empfängerkette unter Verwendung der Konfigurationsdatei zu erstellen, anstatt die Kette programmgesteuert zu erstellen und IDictionary-Objekte an die Konstruktoren zu übergeben.
Dieser Provider fügt keinen Empfänger in die Channelempfängerkette ein. Vielmehr wird nur die Empfängerkette nach kompatiblen Eigenschaften durchsucht. Außerdem kann der Provider problemlos angepasst werden, so dass nur die von einem bestimmten Empfänger unterstützten Eigenschaften akzeptiert werden.
Des Weiteren ist dieser Beispielprovider in Visual Basic geschrieben und wird auch in dieser Sprache kompiliert, während der Rest der Infrastruktur in C# geschrieben ist. Zu Informationszwecken wird auch eine C#-Version des Providers angegeben, dieser wird jedoch durch die angegebenen Befehlszeilen nicht kompiliert.
Vorsicht In der Standardeinstellung führt .NET Remoting keine Authentifizierung und Verschlüsselung aus. Daher empfiehlt es sich, alle erforderlichen Schritte auszuführen, um die Identität von Clients und Servern vor der Remoteinteraktion eindeutig zu ermitteln. Da .NET Remoting-Anwendungen für die Ausführung FullTrust-Berechtigungen erfordern, kann ein unbefugter Client, dem der Zugriff auf Ihren Server gewährt wurde, Code wie mit voller Vertrauenswürdigkeit ausführen. Authentifizieren Sie stets die Endpunkte, und verschlüsseln Sie die Kommunikationsstreams, entweder durch Hosten der Remotetypen in IIS (Internet-Informationsdienste), oder indem Sie für diese Aufgabe ein benutzerdefiniertes Channelempfängerpaar erstellen.
So kompilieren Sie dieses Beispiel und führen es aus
Geben Sie an der Eingabeaufforderung die folgenden Befehle ein:
vbc -r:System.Runtime.Remoting.dll -t:library /out:PropsSink.dll ChannelSinkPropertySetterProvider.vb
csc /r:System.Runtime.Remoting.dll /t:library /out:ServiceClass.dll serviceclass.cs
csc /r:System.Runtime.Remoting.dll /r: ServiceClass.dll /r:PropsSink.dll client.cs
csc /r:System.Runtime.Remoting.dll /r: ServiceClass.dll server.cs
Öffnen Sie zwei Eingabeaufforderungen, die auf dasselbe Verzeichnis zeigen. Geben Sie an der einen Eingabeaufforderung server ein Sobald dieser ausgeführt wird, geben Sie an der anderen Eingabeaufforderung client ein.
Dieser Channelempfängerprovider unterstützt das writeToConsole-Attribut, mit dem festgelegt wird, ob die Aktivitäten des Providers in der Clientkonsole ausgegeben werden. In diesem Beispiel ist das Attribut auf true festgelegt.
Diese Anwendung wird auf einem Einzelcomputer oder über ein Netzwerk ausgeführt. Wenn Sie die Anwendung über ein Netzwerk ausführen möchten, müssen Sie anstelle von "localhost" in der Clientkonfiguration den Namen des Remotecomputers einsetzen.
ChannelSinkPropertySetterProvider.vb
ChannelSinkPropertySetterProvider.cs
Diese Datei wird nur zur Information bereitgestellt.
using System;
using System.Collections;
using System.IO;
using System.Reflection;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.MetadataServices;
// This class implements a client-side channel sink provider that
// walks the channel sink chain, looking for channel sink
// properties that match those specified in the configuration file. If it
// finds them, the provider sets them to the values specified in the
// configuration file. This is a simple helper provider that returns no
// channel itself. Instead, it merely returns the next channel sink it can // find, or null.
public class ChannelSinkPropertySetterProvider : IClientChannelSinkProvider{
private IClientChannelSinkProvider _next = null;
private IDictionary _channelSinkProperties = null;
private ICollection _providerData = null;
// Set the writeToConsole attribute on this provider element in the
// configuration file to "true"; otherwise, information is not written to the console.
private bool _consoleDump = false;
// Default constructor.
public ChannelSinkPropertySetterProvider(){
Console.WriteLine("Default constructor called.");
} // ChannelSinkPropertySetterProvider.
// Constructor with properties. If writeToConsole attribute is "true",
// this constructor will dump all custom configuration properties set
// in the configuration file.
public ChannelSinkPropertySetterProvider(IDictionary properties, ICollection providerData){
_channelSinkProperties = properties;
// Sets the private console dump property for this provider.
if (properties["writeToConsole"] != null)
_consoleDump = Boolean.Parse(properties["writeToConsole"].ToString());
_providerData = providerData;
if (_consoleDump){
Console.WriteLine("ChannelSinkPropertySetterProvider custom constructor called.");
foreach(SinkProviderData sinkData in providerData){
Console.WriteLine("SinkProvider element: " + sinkData.Name);
foreach(DictionaryEntry prop in sinkData.Properties){
Console.WriteLine("Prop name: " + prop.Key.ToString() + " value: " + prop.Value.ToString());
}
foreach(object child in sinkData.Children){
Console.WriteLine("Child: " + child.GetType().Name);
}
}
foreach (DictionaryEntry entry in properties){
Console.WriteLine("channel sink properties: " + entry.Key.ToString() + ", " + entry.Value.ToString());
}
Console.WriteLine();
}
} // ChannelSinkPropertySetterProvider.
// Called by the channel. Normally, this method takes any other sinks
// created by other providers in the chain, links them together, and
// then returns its own sink to the channel. In this case, this
// provider merely sets matching properties on each channel sink in the
// chain, and then returns the **next** channel sink to the channel or
// returns null, indicating to the channel that it is the end of the
// custom channel sink chain.
public IClientChannelSink CreateSink(IChannelSender channel, string url, object remoteChannelData){
if (_consoleDump){
Console.WriteLine("CreateSink is called.");
Console.WriteLine("By " + channel.GetType().Name);
}
IClientChannelSink nextSink = null;
if (_next != null){
nextSink = _next.CreateSink(channel, url, remoteChannelData);
if (nextSink == null){
if (_consoleDump)
Console.WriteLine("Next sink is null!");
return null;
}
WalkSinkChain(nextSink);
}
return nextSink;
}
// This call walks the sink chain, setting properties as it goes.
// The channelSinkProperties are the SinkProviderData dictionaries
// that contain the name of the subnode in the configuration file, and
// a dictionary entry of attribute/value entries on that element.
private void WalkSinkChain(IClientChannelSink thisSink){
if (thisSink == null)
return;
if (_consoleDump)
Console.WriteLine("\r\n\tWalking the sink chain to find sink properties... \r\n");
while(thisSink != null){
if (_consoleDump){
Console.WriteLine(new String('_',80));
Console.WriteLine("Next sink is : " + thisSink.GetType().Name);
DumpSinkProperties(thisSink);
}
SetSinkProperties(thisSink);
thisSink = thisSink.NextChannelSink;
}
return;
}
private void DumpSinkProperties(IClientChannelSink sink){
if (sink.Properties == null){
Console.WriteLine("There are no properties available on the " + sink.GetType().Name + " channelsink.");
return;
}
foreach(DictionaryEntry entry in sink.Properties){
Console.Write("ChannelSink property: " + entry.Key.ToString() + " value: ");
if (entry.Value == null)
Console.WriteLine("No value.");
else
Console.WriteLine(entry.Value.ToString());
}
}
// This method sets properties on the sink.
// The algorithm is that in the absence of instance attribute/value
// entries, the provider element template attribute/value entries will
// be set. This is a simple implementation that does not care about the
// element name underneath the provider element.
private void SetSinkProperties(IClientChannelSink sink){
if (sink.Properties == null){
Console.WriteLine("There are no properties available on the " + sink.GetType().Name + " channelsink.");
return;
}
foreach(DictionaryEntry entry in sink.Properties){
if (_channelSinkProperties.Contains(entry.Key)){
if (_consoleDump)
Console.WriteLine("Setting sink property template on " + sink.GetType().Name + "." + entry.Key.ToString());
sink.Properties[entry.Key] = _channelSinkProperties[entry.Key];
}
}
foreach(SinkProviderData provider in _providerData){
foreach(DictionaryEntry configEntry in provider.Properties){
if (sink.Properties.Contains(configEntry.Key))
if (_consoleDump)
Console.WriteLine("Setting Instance override on " + sink.GetType().Name + "." + configEntry.Key);
sink.Properties[configEntry.Key] = configEntry.Value;
}
}
if (_consoleDump)
DumpSinkProperties(sink);
}
public IClientChannelSinkProvider Next{
get {
return _next;
}
set {
_next = value;
}
}
// This can be called in the constructor in case this provider is
// intended to build its own channel sink provider chain. Without
// providing such a chain, this provider must be specified in a
// configuration file with other providers.
private IClientChannelSinkProvider CreateDefaultClientProviderChain(){
IClientChannelSinkProvider chain = new SoapClientFormatterSinkProvider();
IClientChannelSinkProvider sink = chain;
sink.Next = new BinaryClientFormatterSinkProvider();
sink = sink.Next;
return chain;
} // CreateDefaultClientProviderChain.
} // Class ChannelSinkPropertySetterProvider.
Client.cs
using System;
using System.Runtime.Remoting;
public class Client{
public static void Main(string[] Args){
RemotingConfiguration.Configure("Client.exe.config");
ServiceClass service = new ServiceClass();
Console.WriteLine(service.GetServerTime());
}
}
Server.cs
using System;
using System.Diagnostics;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
public class ServerProcess{
public static void Main(string[] Args){
RemotingConfiguration.Configure("server.exe.config");
Console.WriteLine("Press enter to stop this process.");
Console.ReadLine();
}
}
ServiceClass.cs
using System;
using System.Diagnostics;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
public class ServiceClass : MarshalByRefObject{
private DateTime starttime;
public ServiceClass(){
Console.WriteLine("A ServiceClass has been created.");
starttime = DateTime.Now;
}
~ServiceClass(){
Console.WriteLine("This object is being collected after " + (new TimeSpan(DateTime.Now.Ticks - starttime.Ticks)).ToString() + " seconds.");
}
public DateTime GetServerTime(){
Console.WriteLine("Time requested by a client.");
return DateTime.Now;
}
}
Client.exe.config
<configuration>
<system.runtime.remoting>
<application>
<client>
<wellknown
type="ServiceClass, ServiceClass"
url="https://localhost:8080/RemoteObject"
/>
</client>
<channels>
<channel ref="http">
<clientProviders>
<formatter ref="soap"/>
<provider ref="propsetter" username="bob" writeToConsole="true">
<endpoint allowAutoRedirect="true"/>
<endpoint preauthenticate="true"/>
<endpoint url="example.com:9000" password="xyz" />
<endpoint url="example.com:9001" password="123" />
<endpoint timeout="10000"/>
<endpoint url="example.com:*" username="bob2" password="qwerty" domain="hello" />
</provider>
</clientProviders>
</channel>
</channels>
</application>
<channelSinkProviders>
<clientProviders>
<provider
id="propsetter"
type="ChannelSinkPropertySetterProvider, PropsSink"
/>
</clientProviders>
</channelSinkProviders>
<debug loadTypes="true" />
</system.runtime.remoting>
</configuration>
Server.exe.config
<configuration>
<system.runtime.remoting>
<application>
<service>
<wellknown mode="SingleCall"
type="ServiceClass, ServiceClass"
objectUri="RemoteObject"
/>
</service>
<channels>
<channel port="8080" ref="http" />
</channels>
</application>
</system.runtime.remoting>
</configuration>