Server-Side Messaging

During the creation of the server-side channel (I use the HttpServerChannel in the following description, but the same applies to the TCPServerChannel as well), the server-side sink chain is created and a TcpListener is spawned in a new thread. This object starts to listen on the specified port and notifies the HttpServerSocketHandler when a connection is made.

The message then passes through the layers shown in Figure 7-7.

click to expand
Figure 7-7: Server-side messaging layers

One of the main differences between client-side and server-side message processing is that on the server side, each sink's ProcessMessage() method takes a parameter of type ServerChannelSinkStack. Every sink that is participating in a call pushes itself onto this stack before forwarding the call to the next sink in the chain. The reason for this is that the sinks do not know up front if the call will be handled synchronously or asynchronously. Every sink that's been pushed onto the stack will get the chance to handle the asynchronous reply later.

You can see the IServerChannelSink interface here:

 public interface IServerChannelSink {    IServerChannelSink NextChannelSink { get; }    ServerProcessing ProcessMessage(IServerChannelSinkStack sinkStack,                                         IMessage requestMsg,                                         ITransportHeaders requestHeaders,                                         Stream requestStream,                                         ref IMessage responseMsg,                                         ref ITransportHeaders responseHeaders,                                         ref Stream responseStream);    void AsyncProcessResponse(IServerResponseChannelSinkStack sinkStack,                                  object state,                                  IMessage msg,                                  ITransportHeaders headers,                                  Stream stream);    Stream GetResponseStream(IServerResponseChannelSinkStack sinkStack,                                 object state,                                 IMessage msg,                                 ITransportHeaders headers); } 

HttpServerChannel and HttpServerTransportSink

When a connection to the server-side channel is opened, an instance of HttpServerSocketHandler is created and supplied with a delegate that points to the HttpServerTransportSink's ServiceRequest() method. This method will be called after the background thread finishes reading the request stream.

The HttpServerTransportSink sets up the ServerChannelSinkStack and pushes itself onto this stack before forwarding the call to the next sink.

After the chain has finished processing the message (that is, after the method call has been executed), it generates the HTTP response headers. These will be either "200 OK" for synchronous calls or "202 Accepted" for one way messages.

SDLChannelSink

The SDLChannelSink is a very special kind of sink that really shows the power of the .NET Remoting Framework's extensibility. Contrary to most other sinks, it does not forward any requests to the destination object, but instead generates the WSDL information needed for the creation of proxies.

It does this whenever it encounters either of the strings "?WSDL" or "?SDL" at the end of an HTTP GET request. In this case, the WSDL will be generated by calling the ConvertTypesToSchemaToStream() method from System.Runtime.Remoting.MetadataServices.MetaData.

Note 

MetaData is the same class SoapSuds uses when generating proxies.

When the HTTP request is of type POST or when it is a GET request that does not end with the string "?WSDL" or "?SDL", the message will be passed to the next sink.

SoapServerFormatterSink and BinaryServerFormatterSink

The default server-side HTTP channel uses both formatters in a chained fashion. The first formatter that's used is the SoapServerFormatterSink. This sink first checks whether the serialized message contained in the request stream is a SOAP message. If this is the case, it is deserialized, and the resulting IMessage object is passed to the next sink (which is BinaryServerFormatterSink). In this case, the stream (which is needed as an input parameter to the next sink) will be passed as null.

If the stream does not contain a SOAP-encoded message, it will be copied to a MemoryStream and passed to the BinaryServerFormatterSink.

The binary formatter employs the same logic and passes either the deserialized message (which it might have already gotten from the SOAP formatter, or which it can deserialize on its own) or the stream (if it cannot decode the message) to the next sink.

Both BinaryServerFormatterSink and SoapServerFormatterSink only push themselves onto the SinkStack (before calling ProcessMessage() on the subsequent sink) when they can handle the message. If neither BinaryServerFormatterSink nor SoapServerFormatterSink could deserialize the message and the next sink is not another formatter, an exception is thrown.

DispatchChannelSink

After passing through an optional layer of IMessageSink objects, the message reaches the dispatcher. The DispatchChannelSink takes the decoded IMessage and forwards it to ChannelServices.DispatchMessage(). This method checks for disconnected or timed-out objects and dynamically instantiates SAOs (Singleton or SingleCall) if they do not exist at the server.

After the possible creation of the necessary destination object, it promotes this call to CrossContextChannel.SyncProcessMessage() or CrossContextChannel.AsyncProcessMessage() if the call's target is a one-way method.

CrossContextChannel

The CrossContextChannel notifies dynamic context sinks and passes the IMessage on to ServerContextTerminatorSink. A dynamic sink does not implement IMessageSink, but rather the interface IDynamicMessageSink which is shown here:

 public interface IDynamicMessageSink {     void ProcessMessageStart(IMessage reqMsg, bool bCliSide, bool bAsync);     void ProcessMessageFinish(IMessage replyMsg, bool bCliSide, bool bAsync); } 

The same logic is applied as with client-side dynamic sinks: these sinks do not have to call any additional sinks in the chain, as this will be taken care of by the framework.

The ProcessMessageStart() method is called before passing the message on to ServerContextTerminatorSink and ProcessMesssageFinish() is called after the call to the context terminator sink returns. Both methods may change the IMessage object's properties.

You can read more about dynamic sinks later in Chapter 8.

ServerContextTerminatorSink

This sink's behavior is probably the most complex one. It is the last "hardwired" sink, and therefore has no direct references to other sinks in the chain. So how can the method call specified in the message be executed?

The ServerContextTerminatorSink looks at the IMessage's ServerIdentity object, using InternalSink.GetServerIdentity(), and requests this object's message sink chain using ServerIdentity.GetServerObjectChain().

The ServerIdentity's object chain is populated by a call to the static method CreateServerObjectChain() of the Context class. This call creates a ServerObjectTerminatorSink at the absolute end of the chain and puts other sinks before it. These other sinks are obtained from the DomainSpecificRemotingData object, which is held in the RemotingData property of AppDomain.

DomainSpecificRemotingData by default contains a reference to LeaseSink that will be placed in the chain before the terminator sink. The resulting sink chain, which is obtained from the ServerIdentity object, is shown in Figure 7-8.

click to expand
Figure 7-8: Sinks from the ServerIdentity object

LeaseSink

The lease sink will have a reference to the destination MarshalByRefObject's lease. It simply calls the RenewOnCall() method of this lease and passes the call on to the next sink. The RenewOnCall() method looks at the RenewOnCallTime configuration setting (default of two minutes) and sets the object's time to live to this value.

ServerObjectTerminatorSink and StackbuilderSink

The object terminator sink will forward the call to the StackBuilderSink. This final message sink is a kind of "magic" sink. First it checks if it's okay to remotely call the method specified in the IMessage object. It does this by checking whether the destination object matches the request, meaning that there is a "direct match," or the object implements the requested interfaces or is derived from the requested base class.

After verifying the message, the sink uses several external functions to create a stack frame (that is, it makes the transition between message-based execution and stack-based execution) and calls the destination method. It then generates a return message that contains the result and, if available, the values from any ref or out parameters. This ReturnMessage object is then returned from the call to SyncProcessMessage().




Advanced  .NET Remoting C# Edition
Advanced .NET Remoting (C# Edition)
ISBN: 1590590252
EAN: 2147483647
Year: 2002
Pages: 91
Authors: Ingo Rammer

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net