Export (0) Print
Expand All

Remoting Example: Channel Sink Provider 

The following code example builds a ChannelSinkPropertySetterProvider, which is a channel sink provider that can read values in the client application's configuration file and walk the sink stack looking for channel sinks that contain the properties it found in that file. This is useful to build a sink chain using the configuration file rather than creating the chain programmatically and passing IDictionary objects to the constructors.

This provider does not insert any sink into the channel sink chain. It merely walks the sink chain looking for a set of properties and can easily be customized to take only those properties supported by a specific sink.

Caution noteCaution

.NET Remoting does not perform authentication or encryption by default. Therefore, it is recommended that you take all necessary steps to verify the identity of clients or servers before interacting with them remotely. Because .NET Remoting applications require FullTrust permissions to execute, if a unauthorized client were granted access on your server, the client could execute code as though it were fully-trusted. Always authenticate your endpoints and encrypt the communication streams, either by hosting your remoted types in Internet Information Services (IIS) or by building a custom channel sink pair to do this work.

To compile and run this sample

  • Type the following commands at the command prompt:

    [Visual Basic]

    vbc /t:library /r:System.Runtime.Remoting.dll /out:PropSink.dll ChannelSinkPropertySetterProvider.vb

    vbc /t:library /r:System.Runtime.Remoting.dll serviceclass.vb

    vbc /r:System.Runtime.Remoting.dll /r:ServiceClass.dll /r:PropsSink.dll client.vb

    vbc /r:System.Runtime.Remoting.dll /r:ServiceClass.dll server.vb

[C#]

This channel sink provider supports the writeToConsole attribute to indicate whether you want a console dump of the provider's activities in the client console. For this example, the attribute is set to true.

This application runs on a single computer or across a network. To run this application over a network, replace "localhost" in the client configuration with the name of the remote computer.

ChannelSinkPropertySetterProvider

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 = true;

        // Default constructor.
        public ChannelSinkPropertySetterProvider()
        {
            Console.WriteLine("Default constructor called.");
        } 

        // 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();
            }
        } 

        // 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;
        } 
    } 

Client

[Visual Basic]

Imports System
Imports System.Runtime.Remoting
Imports [Shared]

Public Class Client

    Shared Sub Main()
        RemotingConfiguration.Configure("Client.exe.config", False)
        Dim service As ServiceClass = New ServiceClass()
        Console.WriteLine(service.GetServerTime())
    End Sub

End Class

[C#]

using System;
using System.Runtime.Remoting;

public class Client
{ 
   public static void Main(string[] Args)
   {
      RemotingConfiguration.Configure("Client.exe.config", false);
      ServiceClass service = new ServiceClass();
      Console.WriteLine(service.GetServerTime());   
   }
}

Server

[Visual Basic]

Imports System
Imports System.Runtime.Remoting

Class Server

    Shared Sub Main()
        RemotingConfiguration.Configure("server.exe.config", False)
        Console.WriteLine("Press enter to stop this process.")
        Console.ReadLine()
    End Sub

End Class 

[C#]

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

[Visual Basic]

Imports System
Imports System.Runtime.Remoting

Public Class ServiceClass
    Inherits MarshalByRefObject

    Private starttime As DateTime

    Public Sub New()
        Console.WriteLine("A ServiceClass has been created.")
        starttime = DateTime.Now
    End Sub

    Protected Overrides Sub Finalize()
        Console.WriteLine("This object is being collected after " & (New TimeSpan(DateTime.Now.Ticks - starttime.Ticks)).ToString() & " seconds.")
    End Sub

    Public Function GetServerTime() As DateTime
        Console.WriteLine("Time requested by a client.")
        Return DateTime.Now
    End Function
End Class

[C#]

using System;
using System.Runtime.Remoting;

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="http://localhost:8080/RemoteObject"
         />
      </client>
      <channels>
         <channel ref="http">
         <clientProviders>
            <formatter ref="soap"/>
            <provider ref="propsetter" username="bob" writeToConsole="true">
               <myElement 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, PropSink" 
         />
      </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>

See Also

Community Additions

ADD
Show:
© 2014 Microsoft