Chapter 8: Creation of Sinks

THE PREVIOUS CHAPTER SHOWED YOU the various kinds of sinks and their synchronous and asynchronous processing of requests. What I have omitted until now is one of the most important steps: the instantiation of sinks and sink chains. Sinks are normally not created directly in either your code or with the definition in configuration files. Instead, a chain of sink providers is set up, which will in turn return the sink chains on demand. This chapter shows you the foundation on which to build your own sinks. The implementation of those custom sinks is presented in Chapter 9.

Understanding Sink Providers

As you've seen in Chapter 4, you can define a chain of sinks in a .NET configuration file as shown in the following code. (This example is for a client-side configuration file; for server-side chains, you have to replace clientProviders with serverProviders.)

 <configuration>   <system.runtime.remoting>     <application>       <channels>         <channel ref="http">           <clientProviders>              <provider type="MySinks.SomeMessageSinkProvider, Client" />              <formatter ref="soap" />              <provider type="MySinks.SomeClientChannelSinkProvider, Client" />           </clientProviders>         </channel>       </channels>     </application>   </system.runtime.remoting> </configuration> 

To cover this example more thoroughly, I'll expand the <formatter ref="soap" /> setting using the "real" value from machine.config (including the necessary strong name here):

 <formatter ,    type="System.Runtime.Remoting.Channels.SoapClientFormatterSinkProvider,          System.Runtime.Remoting, Version=1.0.3300.0, Culture=neutral,                        PublicKeyToken=b77a5c561934e089"/> 

The complete chain now looks like this:

 <provider type="MySinks.SomeMessageSinkProvider, Client" /> <formatter ,    type="System.Runtime.Remoting.Channels.SoapClientFormatterSinkProvider,          System.Runtime.Remoting, Version=1.0.3300.0, Culture=neutral,                        PublicKeyToken=b77a5c561934e089"/> <provider type="MySinks.SomeClientChannelSinkProvider, Client" /> 

As you can see in these examples, the chain is not defined using the sinks' names/types (which would be, for example, SoapClientFormatterSink). Instead, a chain of providers is set up. A provider can be either client or server side and has to implement at least one of these interfaces:

 public interface IClientChannelSinkProvider {    IClientChannelSinkProvider Next { get; set; }    IClientChannelSink CreateSink(IChannelSender channel,                                 string url,                                 object remoteChannelData); } public interface IServerChannelSinkProvider {     IServerChannelSinkProvider Next { get; set; }     IServerChannelSink CreateSink(IChannelReceiver channel);     void GetChannelData(IChannelDataStore channelData); } 

You can see in this interface declaration that there is indeed a chain set up using the Next property of each provider.

Creating Client-Side Sinks

After loading the configuration file shown previously, this provider chain will consist of the objects shown in Figure 8-1. The first three providers are loaded from the configuration, whereas the last one (HttpClientTransportSinkProvider) is by default instantiated by the HTTP channel.

click to expand
Figure 8-1: Chain of providers

On the client side, these sink providers are associated with the client-side channel. You can access the channel object using the following line of code:

 IChannel chnl = ChannelServices.GetChannel("http"); 

The "http" in this code line refers to the channel's unique name. For HTTP and binary channels, these names are set in machine.config.

The channel object's contents relevant for creation of the sinks are shown in Figure 8-2.

click to expand
Figure 8-2: IChannel with populated sink providers

When a reference to a remote SAO object is created (for CAOs, an additional ConstructionCall message is sent to the server and the proxy's identity object populated), a lot of things happen behind the scenes. At some time during the use of the new operator or the call to Activator.GetObject(), the method RemotingServices.Connect() is called. What happens after this call is shown (in part) in Figure 8-3.

click to expand
Figure 8-3: Creation of sinks from a chain of providers

After being invoked from RemotingServices, ChannelServices calls CreateMessageSink() on each registered channel until one of them accepts the URL that is passed as a parameter. The HTTP channel, for example, will work on any URLs that start with http: or https:, whereas the TCP channel will only accept those with a tcp: protocol designator.

When the channel recognizes the given URL, it calls CreateMessageSink() on its client-side channel.


HTTP channel internally consists of both the client-side and the server-side transport channel.

HttpClientChannel, in turn, invokes CreateSink() on the first sink provider (as shown in Figure 8-3). What the different sink providers do now corresponds to the following code (shown for a sample SomeClientChannelSinkProvider):

 public class SomeClientChannelSinkProvider: IClientChannelSinkProvider {    private IClientChannelSinkProvider next = null;    public IClientChannelSink CreateSink(IChannelSender channel,                                      string url,                                      object remoteChannelData)    {       IClientChannelSink nextSink = null;          // checking for additional sink providers       if (next != null)       {          nextSink = next.CreateSink(channel,url,remoteChannelData);       }       // returning first entry of a sink chain       return new SomeClientChannelSink(nextSink);    } } 

Each sink provider first calls CreateSink() on the next entry in the provider chain and then returns its own sink on top of the sink chain returned from this call. The exact syntax for placing a new sink at the beginning of the chain is not specified, but in this case the SomeClientSink provides a constructor that takes an IClientChannelSink object as a parameter and sets its _nextChnlSink instance variable as shown in the following snippet (again, only parts of the class are shown here):

 public class SomeClientChannelSink: IClientChannelSink {    private IClientChannelSink _nextChnlSink;    public SomeClientChannelSink (IClientChannelSink next)    {       _nextChnlSink = next as IClientChannelSink;    } } 

The complete sink chain that is returned from the call to ChannelServices.CreateMessageSink() is then connected to a new TransparentProxy/RealProxy pair's identity object, as shown in Figure 8-4.

click to expand
Figure 8-4: The first IMessageSink is connected to the TransparentProxy.

Creating Server-Side Sinks

The creation of server-side sinks works a little differently than the creation of the client-side sinks. As you've seen previously, on the client side the necessary sinks are created when a reference to a remote object is acquired. Contrary to this, server-side sinks are created as soon as a channel is registered.

When the server-side channel is created from a definition in a configuration file, the following constructor will be used:

 public HttpServerChannel(IDictionary properties,                               IServerChannelSinkProvider sinkProvider) 

The IDictionary is taken from the attributes of the <channel> section. When your configuration file, for example, contains this line:

 <channel ref="http" port="1234" /> 

then the properties dictionary will contain one entry with the key "port" and value 1234.

In the sinkProvider parameter to the constructor, the first entry to the chain of sink providers will be passed to the channel. This chain is constructed from the entries of the <serverProviders> setting in the configuration file.

During the channel setup, which is started from the HTTP channel's constructor, one of two things will happen now. If the <serverProviders> setting is missing, the default sink chain, which is shown in Figure 8-5, will be created.

click to expand
Figure 8-5: The HttpServerChannel's default sink chain

When <serverProviders> has been specified in the configuration file, the sink chain will be created from those values, and none of those default sink providers will be used.


This is quite interesting, because in this case, you will not be able to use the "?WSDL" parameter to the URL of your SAO to generate WSDL without explicitly specifying SdlChannelSinkProvider in the <serverProviders> section.

After this chain of providers has been created, ChannelServices.CreateServerChannelSinkChain() is called. This method takes the sink provider chain as a parameter. It then walks the chain and adds a DispatchChannelSinkProvider object at the end of the chain before calling its CreateSink() method. Finally, it returns the generated sink chain. After receiving this object from ChannelServices, HttpServerChannel will add an HttpServerTransportSink as the first element. The resulting server-side channel object is shown in Figure 8-6.

click to expand
Figure 8-6: The complete server-side HTTP channel's sink stack

Advanced  .NET Remoting C# Edition
Advanced .NET Remoting (C# Edition)
ISBN: 1590590252
EAN: 2147483647
Year: 2002
Pages: 91
Authors: Ingo Rammer © 2008-2017.
If you may any questions please contact us: