You are on page 1of 24

Remoting Architecture in .

NET

By Skpananghat
Remoting Architecture in .NET.......................................................................................... 1
By Skpananghat .............................................................................................................. 1
Introduction..................................................................................................................... 1
Remoting......................................................................................................................... 2
AppDomains ................................................................................................................... 2
Marshalling ..................................................................................................................... 3
Marshal by reference: ................................................................................................. 3
Marshal by Value........................................................................................................ 3
Serialization .................................................................................................................... 4
Remoting ........................................................................................................................ 4
Activation Types............................................................................................................. 5
Server-activated types ................................................................................................. 5
Client-activated types.................................................................................................. 6
Lifetime & Lease ............................................................................................................ 6
Remoting Configuration ................................................................................................. 9
Configuration Files ..................................................................................................... 9
System.Runtime.Remoting. RemotingConfiguration class ...................................... 11
RemotingServices.RegisterWellKnownServiceType ............................................... 11
Remoting Architecture.................................................................................................. 12
Proxies: ..................................................................................................................... 13
Channels:................................................................................................................... 14
System.Runtime.Remoting. ChannelServices .......................................................... 15
Channel Sinks ........................................................................................................... 15
Formatters ................................................................................................................. 16
Dispatcher ................................................................................................................. 18
Declaring Methods "One-Way" .................................................................................... 20
The Call Context ........................................................................................................... 20
Remoting Services ........................................................................................................ 20
Known Common Error Messages................................................................................. 21
System.Runtime.Remoting.TrackingServices .......................................................... 21
Asynchronous Remoting............................................................................................... 22
Calling Remote Methods Asynchronously ............................................................... 22
Call backs with Delegates......................................................................................... 23

Introduction
This is an in-depth, look at Remoting infrastructures in the Common Language Runtime
and the .NET Framework. This study provides detailed information of the
implementation of Remoting. I have givenonly a small description about each topic or
else it eat up very huge number of pages. And since i am not that much good in graphics,
I've grabbed the required images from other sources to depict some situation in this
document .

Remoting
NET remoting enables you to build widely distributed applications easily, whether the
application components are all on one computer or spread out across the entire world.
You can build client applications that use objects in other processes on the same
computer or on any other computer that is reachable over its network. In short NET
remoting provides an abstract approach to inter-process communication

Before getting deep into Remoting functionalities it would be good if I explain about
some other terms in .Net

AppDomains
An application domain is a partition in operating system process where one or more
applications reside or in other words Application domains are the isolated environments
where managed applications execute. .Net is having the concept of Appdomains just to
decrease the overhead of Win32 process isolation and memory handling tasks. You can
create as many Appdomains as you need and there will be a default "Appdomain". The
applications running under CLR (Managed environment) can be accessed using the
"Appdomain.CurrentDomain" property.

Thus Appdomains keeps the required assemblies separate with in the same Win32
process. Objects in the same application domain communicate directly. Objects in
different application domains communicate either by transporting copies of objects across
application domain boundaries, or by using a proxy to exchange messages. This is
accomplished by the process of marshalling and serialization

Marshalling
Marshalling and unmarshalling refers to packing and unpacking of parameters and return
values of a particular method call. There are 2 types of marshalling available in .Net.
Marshal by reference and Marshal by value. A simple synonym for marshalling would
be "packaging data for transfer".

Marshal by reference:

The first time an application in a remote application domain accesses a


MarshalByRefObject, a memory reference proxy is passed to the remote application.

A proxy is an object that exists and acts locally on behalf of a remote object. The proxy
looks like and accepts calls as if it where the "real" object.

The proxy exposes the same set of interfaces that original object exposes. However, the
proxy does not process the calls locally, but it holds the address or path to the actual
objects and thus sends them to the remote object it represents

Subsequent calls on the proxy are marshalled back to the object residing in the local
application domain. The same process is maintained regardless of whether the remote
application resides in a same physical machine, in same process or different physical
machine

This process of communicating between Appdomains is called Remoting, and we can


come to this point later on.

In order to make an object a MarshalByRef component you need to inherit it from


MarshalByRefObject class which internally handles the marshalling and lifetime
manipulation task of that object. MarshalByRefObject is the base class for objects that
communicate across application domain boundaries by exchanging messages using a
proxy.

Marshal by Value

Objects that do not inherit from MarshalByRefObject are implicitly marshal by value.
When a remote application references a marshal by value object, a copy of the object is
passed across application domain boundaries.

The basic difference is that a MarshalByRef component is not usable outside the
application domain where they were created where MarshalByvalue component is usable
just because it is having its state packed along.
Note: - Static variables and static methods cannot be proxied; instead it acts a marshal by
value sending a copy of the whole stuff to the client and will be executed in the clients
context.

Serialization
Serialization is the process of converting the state of an object into a form that can be
persisted or transported. The complement of serialization is deserialization, which
converts a stream into an object. Together, these processes allow data to be easily stored
and transferred.

The .NET Framework features two serializing technologies:

Binary serialization preserves type fidelity, which is useful for preserving the state of an
object between different invocations of an application. For example, you can share an
object between different applications by serializing it to the Clipboard. You can serialize
an object to a stream, to a disk, to memory, over the network, and so forth. Remoting uses
serialization to pass objects "by value" from one computer or application domain to
another.

XML serialization serializes only public properties and fields and does not preserve type
fidelity. This is useful when you want to provide or consume data without restricting the
application that uses the data. Because XML is an open standard, it is an attractive choice
for sharing data across the Web. SOAP is likewise an open standard, which makes it an
attractive choice.

Remoting ...
Now that you got some knowledge about "Appdomains" and all, we can continue with
remoting. As explained before, remoting provides an abstract approach to inter-process
communication. In either case if your remote objects reside in entirely different or same
physical machine, you need to host that application using some hosts which describes the
type, location and other simple metadata about that object

You must provide the following information to the remoting system to make your type
remotable:

• The type of activation required for your type.


• The complete metadata that describes your type.
• The channel registered to handle requests for your type.
• The URL that uniquely identifies the object of that type. In the case of server
activation, this means a Uniform Resource Identifier (URI) that is unique to that
type. In the case of client activation, a URL that is unique to that instance will be
assigned

You can provide this information either programmatically or through application


configuration files. So that you can change the configuration in future to point to any of
the other objects or machine without touching or recompiling the code.

The methods that will be called from the client are implemented in a remote object class.
In the figure below we can see an instance of this class as the Remote Object. Because
this remote object runs inside a process that is different from the client process – usually
also on a different system – the client can't call it directly. Instead the client uses a proxy.
For the client, the proxy looks like the real object with the same public methods. When
the methods of the proxy are called, messages will be created. These are serialized using
a formatter class, and are sent into a client channel. The client channel communicates
with the server part of the channel to transfer the message across the network. The server
channel uses a formatter to deserialize the message, so that the methods can be
dispatched to the remote object:

Activation – Remote objects can be activated using the new operator. Of course, there
are other ways to activate remote objects like Activator.GetObject(),
Activator.Createinstance() etc which will vary according to the activation type.

Activation Types
There are two types mainly Server activated types and client activated types

Server-activated types

Server-activated objects are objects whose lifetimes are directly controlled by the server.
The server application domain creates these objects only when the client makes a method
call on the object rather than when the client calls new or Activator.GetObject;
There are two activation modes (or WellKnownObjectMode values) for server-activated
objects: Singleton and SingleCall

Singleton types, as you know, never have more than one instance at any point of time. If
an instance exists, all client requests are serviced by that instance. If an instance does not
exist, the server creates an instance and all subsequent client requests will be serviced by
that instance.

SingleCall types always have one instance per client request. The next method
invocation will be serviced by a different server instance, even if the previous instance
has not expired in the system. SingleCall types do not participate in the lifetime lease
system.

To create an instance of a server-activated type, clients either configure their application


programmatically or use a configuration file and then pass the remote object's
configuration (Like type and object URI) in a call to Activator.GetObject.

Client-activated types

Client-activated objects are objects whose lifetimes are controlled by the calling
application domain, just as they would be if the object were local to the client.

Client-activated objects use lifetime leases to determine how long they should continue to
exist

Lifetime & Lease


Before telling about lease I would like to tell some thing about the garbage collection in
.net

The .NET Framework's garbage collector manages the allocation and release of memory
for your application. Each time you use the new operator (or
Activator.CreateInstance) to create an object, the runtime allocates memory for the
object from the managed heap. As long as address space is available in the managed
heap, the runtime continues to allocate space for new objects. However, memory is not
infinite. Eventually the garbage collector must perform a collection in order to free some
memory. The garbage collector's optimizing engine determines the best time to perform a
collection, based upon the allocations being made. When the garbage collector performs a
collection, it checks for objects in the managed heap that are no longer being used by the
application (the objects that have marked as unreachable in its object header) and
performs the necessary operations to reclaim their memory.

In our scenario, the client has reference to a proxy of the remote object in heap and there
should be a way by which the server can determine whether the reference to this server
object is still needed for the application. We achieve this particular function by
maintaining a leased lifetime for each server activated singleton and client activated
objects

In short leasing and sponsorship is the solution for managing the lifecycle of a remote
object in .NET. Each object has a lease that prevents the local garbage collector from
destroying it, and most distributed applications rely upon leasing. A sponsor is a third
party that the .NET Remoting Framework consults when a lease expires, giving that party
an opportunity to renew the lease

Whenever an MBR (Marshal-by-reference objects) object is remoted outside an


application domain, a lifetime lease is created for that object. Each application domain
contains a lease manager that is responsible for administering leases in its domain. A
server object can have many sponsors associated with its lease. The lease manager keeps
a list of all the sponsors. The lease manager periodically examines all leases for expired
lease times. If a lease has expired, the lease manager sends a request to its list of sponsors
for that object and queries whether any of them wants to renew the lease. If no sponsor
renews the lease, the lease manager removes the lease, the object is deleted, and its
memory is reclaimed by garbage collection. An object's lifetime, then, can be much
longer than its lifetime lease, if renewed more than once by a sponsor or by continually
being called by clients.

The Default lease time for an object is 5 Minutes, which is configured by overriding
MarshalByRefObject.InitializeLifetimeService and specifying our own
TimeSpan object as required

public override ILease InitializeLifetimeService ()


{
ILease lease = (ILease) base.InitializeLifetimeService ();
if (lease.CurrentState == LeaseState.Initial)
{
lease.InitialLeaseTime = TimeSpan.FromMinutes (1);
lease.SponsorshipTimeout = TimeSpan.FromMinutes (2);
lease.RenewOnCallTime = TimeSpan.FromSeconds (2);
}
return lease;
}
If the lease is about to expire, the .NET Remoting Framework will automatically extend
the lease on every call by the value set in the MarshalByRefObject. RenewOnCallTime
property. The default call time renewal is two minutes. The value of the current lease
time (the time the object has to live unless the lease is extended) is a product of the lease
time (the initial lease time for the first time) and renew on call time, according to the
formula shown in the following line of code:

Current lease time = MAX (lease time - expired time, RenewOnCallTime)

The RenewOnCallTime will have an effect only if it is greater than the lease time minus
the expired time. In that case, the expired time property is reset, and the lease time is set
to the RenewOnCallTime. The result is that even if an object is very busy, its lease time
does not grow in proportion to the amount of traffic it has. Even if the object has a
temporary spike in its load, its lease will expire after some quiet time.

If you know that a particular object will not be called for a longer period of time than the
default timeouts but you do not want to change the mechanism globally, you can create a
"sponsor" for that object and register it on the object's ILease interface that you can
obtain through the GetLifetimeService() method that every AppDomain bound,
MarshalByRefObject derived class and every contextful class inherits.

If an object's lease expires, the object is marked for garbage collection and all existing
remote references to this object become invalid.

The sponsor must implement the ISponsor interface, defined as:

public class ClientSponsor: MarshalByRefObject,ISponsor


{
public TimeSpan Renewal(ILease lease)
{
Debug.Assert(lease.CurrentState == LeaseState.Active);
//Renew lease by 5 minutes

return TimeSpan.FromMinutes(5);
}
}

After this we need to create the instance of the remotable object and register it with a
sponsor as follows

ISponsor sponsor = new ClientSponsor ();


MyClass obj = new MyClass();
// or MyClass obj = Activator.GetObject(typeof(MyClass), url)

//Register the sponsor

ILease lease = (ILease)RemotingServices.GetLifetimeService(obj);


lease.Register(sponsor);

The lease manager calls ISponsor's single method, Renewal, when the lease expires,
asking for new lease time

If the exception RemotingException: Object <URI> has been disconnected or does not
exist at the server occurs, this may be because of an expired lease time.

Remoting Configuration
Configuration Files

Both server and client channel can be configured programmatically or by using a


configuration file.

Using configuration files for remoting clients and servers has the advantage that the
channel and remote object can be configured without changing a single line of code and
without the need to recompile. Another advantage is that the remoting code we have to
write is very short.Specifying the options programmatically has the advantage that we
could get to the information during runtime. One way to implement this can be that the
client uses a directory service to locate a server that has registered its long running
objects there.

The server configuration file will look like this. We can call this file as SimpleServer.
exe.config.

<configuration>
<system.runtime.remoting>
<application name="SimpleServer">
<service>
<wellknown
mode="SingleCall"
type="Samples.MyRemoteObject, MyRemoteObject"
objectUri="MyRemoteObject" />
</service>
<channels>
<channel ref="tcp server" port="9000" />
</channels>
</application>
</system.runtime.remoting>
</configuration>

All the server has to do is read the configuration file and activate the channel. This can be
done with a single call to the static method RemotingConfiguration.Configure().
RemotingConfiguration.Configure() reads the configuration file
SimpleServer.exe.config to configure and activate the channel. The creation of the remote
object and communication with the client is done by the remoting infrastructure. We just
have to make sure that the process doesn't end. We do this with Console.ReadLine()
that will end the process when the user enters the return key:

// SimpleServer.cs

using System;
using System.Runtime.Remoting;
namespace Samples
{
class SimpleServer
{
static void Main(string[] args)
{
RemotingConfiguration.Configure("SimpleServer.exe.config");
Console.WriteLine("Press return to exit");
Console.ReadLine();
}
}
}

The client Channel would look like as follows. We can call this file as
SimpleClient.exe.config

<configuration>
<system.runtime.remoting>
<application name="SimpleClient">
<client url="tcp://localhost:9000/SimpleServer">
<wellknown
type="Samples.MyRemoteObject, MyRemoteObject"
url =
"tcp://localhost:9000/SimpleServer/MyRemoteObject"
/>
</client>
<channels>
<channel ref="tcp client" />
</channels>
</application>
</system.runtime.remoting>
</configuration>

As in the server, we can activate the client channel by calling


RemotingConfiguration.Configure(). Using configuration files we can simply use
new to create the remote object. Next we call the method Hello() of this object:

// SimpleClient.cs

using System;
using System.Runtime.Remoting;
namespace Samples
{
Public class SimpleClient
{
static void Main(string[] args)
{
RemotingConfiguration.Configure("SimpleClient.exe.config");
MyRemoteObject obj = new MyRemoteObject();
Console.WriteLine(obj.Hello());
}
}
}

System.Runtime.Remoting. RemotingConfiguration class

The RemotingConfiguration class provides facilities for Remoting configuration as well


as methods to register "well known objects" and client-activation objects

To expose (or publish) objects to clients, you need to register them with the
RemotingServices class using a "well-known" name. The RemotingServices itself bind
any registered object to the channels registered on the ChannelServices, so that each
object gets a "well-known" endpoint.

"Well-known" means that once you make that information available through your
program documentation, web-site, DISCO documents or UDDI, the clients can connect to
an object using a published and, hence, well-known name.

Objects can only be published using these well-known names and therefore the COM
concept of programmatic identifiers (ProgIDs) or class-identifiers (CLSIDs) is neither
supported nor required

RemotingServices.RegisterWellKnownServiceType

This is the way by which we register Singleton or Single Call objects programmatically
(without the help of configuration files). This is done with a call to
RegisterWellKnownServiceType(). That you register an object is, in fact, not entirely
accurate. You register the "type" (or better: class) of the object and the
RemotingConfiguration infrastructure will use the type information to either create a new
instance of that class for every call or to immediately create a new instance to be used as
a singleton object. In neither case you can pass arguments to the object at creation time –
just as in COM.

The example shown here illustrates the registration of a well-known object and the
resulting URI that a client can use to connect to the object's endpoint.

Registering a well-known object and making it available for remote clients doesn't take
more than three simple steps:

1. Create a new channel object instance, providing an IP port to listen on or some


other address information, if you have a custom channel.
2. Register the channel with the ChannelServices class.
3. Register the class of the object and the endpoint identifier, which must be unique
for your application. The last argument for the call indicates whether the object
shall be single call or singleton. Here, it's a SingleCall object.

The result from these registration steps is that your .NET application exposes an object
on the shown endpoint: "http://myserver:8080/MyEndpointURI".

Now your .NET application is independent of whether it is a service process, a Windows


Forms application or Console application is a full-blown HTTP server without using
Internet Information Server or another HTTP infrastructure. You can hit this address with
a standard web-browser appending "?WSDL" to the endpoint address and will see – the
WSDL service contract.

Remoting Architecture
The whole remoting architecture comprises of Messages, sinks, Proxies and configuration
objects.

Messages are objects that implement IMessage


Messages are the objects through which the information is transferred to another object.
From a programming standpoint, messages are objects that implement the IMessage
interface.

IMessage: The IMessage interface is a very simple dictionary of key/value pairs. The
message dictionary may contain any number of entries, each individually keyed by a
string. The values that are held in the message may be of any .NET object type. The
Remoting infrastructures task is to remote these dictionaries across Remoting boundaries

To make implementation of method calls consistent, .NET defines a few additional


message types that all implement the IMessage interface. The message types include
construction call messages and responses as well as method call messages and responses

If messages are being delivered synchronously, they represent a request that expect an
immediate response. If they are delivered asynchronously they represent a request that
expects a delayed response or no response at all.

Proxies:

As told earlier the proxy exposes the same set of interfaces that original object exposes.
However, the proxy does not process the calls locally, but it holds the address or path to
the actual objects and thus sends them to the remote object it represents

There are mainly two types of proxies used in the remoting context: The "RealProxy"
and the "Transparent proxy".

Transparent Proxies

A transparent proxy is a dynamically created block of code that exists in the client
application domain (or context) on behalf of the remote object and exposes all of its
features, including all fields, properties, events, delegates and methods. The transparent
proxy is an instance of the class System.Runtime.Remoting.Proxies.__
TransparentProxy. This class is internal to the mscorlib assembly, so we cannot derive
custom proxies from it

When you call a method of the transparent proxy, the proxy formats all of the arguments
and the method name into a message object and submits this through the real proxy's
Invoke() method to the remote object. Once the method returns, it decodes the message
elements and places them back on the local stack so that the calling code can pick them
up.
This diagram illustrates the dependencies between the real proxy and the transparent
proxy as well as the message sink and the channel. A client that uses an object across any
kind of a remoting boundary is actually using a transparent proxy for the object. The
transparent proxy provides the illusion that the actual object resides in the client's space.
It achieves this by forwarding calls made on it to the real object using the remoting
infrastructure

RealProxy

A RealProxy is an instance of a class that inherits from the abstract base class RealProxy
that is defined in the System.Runtime.Remoting namespace.

The real proxy accepts messages (IMessage) through its Invoke() method and forwards
them to a remote object. With this functionality the real proxies are the transport layer for
transparent proxies. However, they not only do the bulk of the work for the transparent
proxies, they are also the "factories" to create them. The RealProxy base class is able to
dynamically create a transparent proxy for any arbitrary .NET class-type through
metadata and .NET Reflection and have that transparent proxy call the real proxy's own
Invoke() method.

The RealProxy implements a part of the functionality that is needed to forward the
operations from the transparent proxy. Note that a proxy object inherits the associated
semantics of managed objects such as garbage collection, support for fields and methods,
and can be extended to form new classes. The proxy has a dual nature: it acts as an object
of the same class as the remote object (transparent proxy), and it is a managed object
itself.

Channels:

Channels are responsible for transporting messages. As such, they are an abstraction of a
specific transport protocol and wrap the protocol to make it available to the .NET
Remoting infrastructure.
The .NET Framework comes with three built-in sets of channels: A TCP channel that
uses permanently-connected raw TCP/IP sockets and a HTTP channel that uses the
.NET HTTP infrastructure.

The channel model is also extensible; if you require support for other transport protocols,
you can implement your own channels and use them with all the parts of the .NET
Remoting infrastructure.

A channel is responsible for establishing endpoint-to-endpoint communication. The


transport portion of the channel implementation is handed a formatted data stream and is
responsible for getting that formatted data stream to the other side. On the other hand, it
will receive a formatted data stream from another endpoint and is responsible to deliver
that to its host application domain.

Channels can listen for messages and send messages. The listener portion of a channel
implements the IChannelReceiver interface and the sender portion implements the
IChannelSender interface. A channel is only bi-directional it implements both
interfaces. If you want, for some reason, to implement a receive-only channel you only
need to implement the IChannelReceiver interface and IChannelSender for a send-
only channel, respectively. A receive-only channel can, of course, not relay any call
backs.

As previously mentioned, you can also write your own channels, if you need to transmit
data over protocols not covered by the core .NET Framework, for instance, APPC, IPX or
Windows Named Pipes.

For your own channel implementation you must implement either the
IChannelReceiver or the IChannelSender interfaces, or both, depending on whether
the channel is going to be bi-directional or unidirectional.

System.Runtime.Remoting. ChannelServices

The ChannelServices class serves to manage and register .NET Remoting channels. You
can register any number of channels with the channel services. These channels are global
for your application domain and define the transport endpoints that your application
exposes.

If you register a TCP channel on port 8085 and a HTTP channel on port 8080, all object
endpoints that have been registered with RemotingServices will be equally available on
both channels

Channel Sinks

Most of the functionality that was explained above will not work until this is actually
implemented in a chain of channel sinks. A channel sink is a pluggable component that is
provided to the channel through a channel sink provider class.
A channel sink provider class implements the interface IClientChannelSinkProvider
if it generates sinks for the client side of a channel and implements
IServerChannelSinkProvider if it generates sinks for the server side. The sinks
themselves implement IClientChannelSink and or IServerChannelSink.

When a channel is created, you will, as in the case of the Http Channel or Tcp Channel,
be able to pass an implementation of either or both interfaces to the channel, letting you
install your own channel sink.

Channel sinks may implement interception, security checks, logging or whatever else you
want to control or monitor regarding Remoting traffic.

A very special case is of formatter sinks. These are provided by implementations of either
IClientFormatterSinkProvider or the IServerFormatterSinkProvider interfaces
and perform the conversion from and to the wire-format. A formatter sink implements
IClientFormatterSink and/or IServerFormatterSink.

When a channel is constructed, the providers are asked to provide channel sinks, which
are then linked into a chain of sinks. A call from a client will then be routed from the
proxy to a formatter sink (or a custom formatter sink that is placed before the formatter
sink), then to possibly existing custom sinks and finally to the transport sink that will
typically be provided by the channel implementation. On the receiving end, the channel
architecture works similarly in the other direction.

Formatters

Formatters are dynamically used by the channel architecture. You configure a channel to
transport messages in a certain wire format by associating it with a formatter other than
its default through the Remoting configuration facility. The channel will then pick the
associated formatter to render messages into the desired format.

Formatters are implemented through so-called sinks. Essentially, a channel employs a


sequence of these sinks, which work hand-in-hand to inspect an IMessage for logging and
security purposes, turn it into a wire-format and send it off to the receiver.

On the receiving end, the formatter acts as a de-serializer component that understands a
certain wire format and is able to translate that into a .NET message object implementing
IMessage.

The .NET Framework has two built-in formatters namely binary and SOAP. The binary
formatter uses a very compact binary format that is designed for best performance in
high-speed LAN environments and to interconnect .NET systems. The SOAP formatter
uses the XML-based Simple Object Access Protocol to transport object calls over the
Internet and to integrate .NET systems with all other systems in the enterprise
environment. The built-in formatter types are, of course, pre-registered with the system.
Custom Formatters allow talking to any endpoint

The formatter model is extensible. If you want to integrate systems directly using other
wire formats (to name a few: IIOP, RMI, ORPC, XP, XML-RPC) you could implement
your own formatters and have .NET systems talk natively to any endpoint you want.

TCP channel uses plain TCP sockets and transmits, by default, a compact, binary wire
format, provided by the BinaryFormatter object.

However, if you want to link .NET Framework-based systems in a LAN environment, the
TCP channel and binary formatter will yield the highest performance

The default wire format is an implementation of the SOAP 1.1 standard, which is
provided by the SoapFormatter class located in the namespace "System.Remoting.
Serialization.Formatters.Soap".

By now we have learned about messages that are key/value pair dictionaries and about
channels that somehow receive these messages, pass them to formatters to render them
into a wire format and then transport them to the destination.

On the receiving end, the wire format message is received by a channel and deserialized
by the formatter into the message object.

To use channels, client code drops off messages into the Remoting infrastructure using
the SyncDispatchMessage() or AsyncDispatchMethod() method on the
ChannelServices class, which resides in the System.Runtime.Remoting namespace.
As its name implies, SyncProcessMessage processes the request message in a
synchronous manner by passing that message to the next sink's SyncProcessMessage
method, where NextSink is a property in the same class pointing to the next sink in the
channel sink chain.

The IMessageSink interface is implemented by channels and the Remoting infrastructure


to accept messages

The IMessageSink interface has a NextSink property, which enables chains of message
sinks to be built. Message chains are a very common design pattern throughout the
Remoting infrastructure. When implementing your own message sink you must therefore
honour the NextSink property, by passing onward to the next sink any message which
you do not want to handle yourself.

Member Member Type Description


NextSink Read-only The next message sink in the chain, or null if this
property is the last sink in the chain
AsyncProcessMessage Method Processes the message asynchronously
SyncProcessMessage Method Processes the message synchronously

It is, however, perfectly legal to intercept, modify or process any message received in a
message sink implementation instead of passing it through to the ultimate destination.

If an unbound class has no serialization support, it cannot be marshalled and will cause a
remote method invocation to fail, if it as used as an argument or return value.

Adding serialization support to a class requires setting the "[serializable]" attribute


on the class and, optionally, implementing the ISerializable interface of the class. This
allows Formatters to use the serialization support of the Runtime to render the class
content in XML or a binary data stream, which can then be translated into the formatter's
native wire format.

Dispatcher

The Dispatcher is the proxy's counterpart at the receiving end of the communication and
located at the channel endpoint.

The Dispatcher's capabilities are provided by the StackBuilderSink class that is located
in the System.Runtime.Remoting.Messaging namespace.

The dispatcher

• Receives messages,
• builds stack-frame,
• invokes method
• Collects result
• and creates response message

Whenever one of those two methods (Method A or Method B shown in the above figure)
is called, the passed message object is decoded and the Dispatcher locates the object it
that is referenced in the call, dynamically creates a stack frame that contains all
arguments and executes the requested method.

When the method call returns, it collects the output parameters and the return value and
creates a response message with these parameters. The dispatcher will, of course, convert
marshal by reference object into references, which in turn are passed over the network
and "proxied" on the receiving end. The response message is then handed back to the
caller.

Below is a graphical representation of the flow. The message which arrives at the
receiving end of a channel, is serialized and formatted into a message object and is passed
to the StackBuilderSink.SyncDispatchMessage() method. The call is dispatched on
the real object and all values that must be returned are collected and sent back as a
response through the channel.

Let us assume we already have a transparent proxy that we can call. When we call the
method "MethodA()" on the transparent proxy, it formats the method name and the
arguments if such are present, into a IMessage format, and forwards them to the real
proxy's Invoke() method. The real proxy then drops this message into the
IMessageSink of the channel through the sink's SyncProcessMessage() method. Since
.NET makes no assumptions about the endpoint architecture, we're not doing this either,
and just assume that the message will somehow be responded. Actually this message
object will be parsed by each sink shown in the above figure until the sink chain ends or
server object reaches and in a similar manner it navigates back to the client object too.
Once the real proxy receives the response message, it will pass the response message on
to the transparent proxy, which will push the return values on the stack. With that, the
calling code will believe that it just called a local method.

When a channel receives a message that contains an object reference (an instance of the
ObjRef class), the reference information also contains the type of the referenced object.
With that data type in hand, the channel will create a new RealProxy instance and asks it
to create a new transparent proxy based on the metadata of that data type. This
transparent proxy object is then passed on to the called method
Declaring Methods "One-Way"
You can declare methods one-way using the "[oneway]" attribute on the message. When
this attribute is present on the method, the dispatcher will not collect any return value
information and the proxies will not wait for such information to arrive and return
immediately to the caller.

If a OneWay method throws an exception this exception will not be propagated to the
client.

The Call Context


A very valuable tool is the call context. (Do not confuse this with the .NET Remoting
context) The call context allows you to add invisible arguments to a method call. While
there will only be rare uses in end-to-end communication, the call context is a true
lifesaver when you are using context interception and take advantage of the extensibility
mechanisms and the message chain that we are going to discuss in the now following
section.

Each message object automatically contains a dictionary entry "__CallContext" that


holds an instance of the type "CallContext"

The CallContext class is a utility class in the namespace System.Runtime.


Remoting.Messaging. We can add data to the context with CallContext.SetData(),
and reading the data can be done with CallContext.GetData().

A class that can be passed with the call context must implement the interface
ILogicalThreadAffinative. This interface doesn't define a single method; it is just a
marker to the .NET Remoting runtime. A logical thread can spawn multiple physical
threads as such a call can cross process boundaries, but it is bound to a flow of method
calls. Implementing the interface ILogicalThreadAffinative in a class means that
objects of this class can flow with the logical thread. Additionally, the class is marked
with the [Serializable] attribute, so that the object can be marshaled into the channel

Remoting Services
The System.Runtime.Remoting namespace contains a couple of so-called service
classes that allow you to configure and tweak the runtime behaviour of the Remoting
system.

If the activator has determined that an object needs to be created or accessed remotely, it
forwards the call to the Connect() method on the RemotingServices class.
Connect() provides low-level access to Remoting and lets you activate objects remotely
even if the class is not configured to be remote in your application domain. The
Connect() method takes an argument of type System.Type (or a string representation
thereof) and an endpoint URI as arguments. The return value is a properly proxied object,
if the call succeeds.

The type information is used to extract the metadata from an Assembly to create the
transparent proxy from the real proxy. Therefore, the metadata of the remote class must
be available to the client at runtime. This does not mean, though, that the client must
possess the actual server implementation Assembly. The client side may rather have a
reduced copy of the metadata that only describes the full structure and that is sufficient
for the proxy to build a structural image of the remote class.

One way to do this is to use the soapsuds utility that is included with the .NET
Framework SDK. The easiest way to build a metadata-only Assembly is:

soapsuds –ia:<inputAssemblyName> -oa:<outputFile>

Known Common Error Messages


If the connection string that is used with the Connect() method is wrong, we get an error
the first time that a remote method is called. The possible error messages we can get are
listed here:

1.SocketException: No such host is known.

This error message occurs when the host name cannot be resolved.

2.SocketException: No connection could be made because the target machine actively


refused it.

If the specified port number is incorrect or the server is not started, we will get this error
message.

3.RemotingException: Object <Name> has been disconnected or does not exist at the
server.

This error message occurs if the name of the endpoint is incorrectly specified, or when
the leasing time has expired when using a client-activated object.

System.Runtime.Remoting.TrackingServices
The tracking services serve to "track" almost everything that is happening in the
Remoting subsystem. Whenever an object becomes disconnected (after its lease has
expired or a connection is broken) or when an object has been marshalled or unmarshaled
(a proxy has been built), the tracking services will invoke a user provided implementation
of the ITrackingHandler interface. This interface defines three methods that are called
by the remoting infrastructure when marshalling and unmarshalling occurs:

We can register an implementation of ITrackingHandler by the following code snippet

TrackingServices.RegisterTrackingHandler(new MyTrackingHandler());

Where MyTrackingHandler is the implementation of ITrackingHandler

Asynchronous Remoting
Calling remote methods across the network can take some time. We can call the methods
asynchronously, which means that we start the methods, and during the time the method
call is running on the server we can do some other tasks on the client. .NET Remoting
can be used asynchronously in the same way as local methods.

Calling Remote Methods Asynchronously

The same programming model used for local calls can also be used for remote calls. We
will extend the remoting example by using some arguments and return values with a
remote method. First, we extend the remote object class RemoteObject with the long-
running method MyMethod(). Nothing else changes with the server.

// RemoteObject.cs

// ...

public int Mymethod(int val1, int val2, int ms)


{
Thread.Sleep(ms);
return val1 + val2;
}

In the client program, we have to declare a delegate with the same parameters as the
Mymethod() method:
// SimpleClient.cs

// ...

class SimpleClient
{
private delegate int MyMethodDelegate(int val1, int val2,int ms);

In the Main() method after creating the remote object we create an instance of the
delegate and pass the method Mymethod to the constructor:

static void Main(string[] args)


{ RemotingConfiguration.Configure("SimpleClient.exe.config");
RemoteObject obj = new RemoteObject();
MymethodDelegate d = new MyMethodDelegate (obj.Mymethod);
}
}
The remote method can now be started asynchronously with the
BeginInvoke()
method of the delegate class. Here we pass the values 3 and 4 to add
with a
sleep time of 100 ms. The method BeginInvoke() will return the control
immediately initialising the execution of Mymethod method; The main
thread
will continue it's execution with other statemets.
IAsyncResult ar = d.BeginInvoke(3, 4, 100, null, null);
Console.WriteLine("Method started");

To get the result that is returned with the method Mymethod() we have to wait until the
method is completed. This is done using ar.AsyncWaitHandle.WaitOne(). Calling the
delegate method EndInvoke() we get the result that is returned from Mymethod().

ar.AsyncWaitHandle.WaitOne();
if (ar.IsCompleted)
{
Console.WriteLine("Method finished");
int result = d.EndInvoke(ar);
Console.WriteLine("result: " + result);
}

Call backs with Delegates

.NET Remoting supports two types of call backs: with remote objects that are passed to
the server, and with delegates. Using a delegate we can pass an AsyncCallback delegate
when calling the method BeginInvoke() to have a call-back when the asynchronous
remote method completes. We have to define a method that has the same signature and
return type that the delegate AsyncCallback defines. The delegate AsyncCallback is
defined with this signature in the assembly mscorlib:

public delegate void AsyncCallback(IAsyncResult ar);


We implement the method MyMethodCallback that has the same signature and return
type. The implementation of this method is similar to how we handled the asynchronous
method in the last code sample. It doesn't matter if the method is implemented as a class
method or an instance method. We only need access to the delegate that is used to start
the remote method. To make the delegate available to the static method we'll now
declared as static member variable:

private delegate int MymethodDelegate(int val1, int val2, int ms) {


private static MymethodDelegate d;
public static void MymethodCallback(IAsyncResult ar)
{
if (ar.IsCompleted)
{
Console.WriteLine("Method finished");
int result = d.EndInvoke(ar);
Console.WriteLine("result: " + result);
}
}
}

In the Main() method, we have to create a new instance of the AsyncCallback delegate
and pass a reference to the function that should be called asynchronously. With the
BeginInvoke() method we pass the instance of the AsyncCallback so that the method
MymethodCallback() will be invoked when the remote method Mymethod() completes

static void Main(string[] args)


{
RemotingConfiguration.Configure("SimpleClient.exe.config");
RemoteObject obj = new RemoteObject();
d = new MymethodDelegate(obj.Mymethod);
AsyncCallback cb =
new AsyncCallback(SimpleClient.MymethodCallback);
IAsyncResult ar = d.BeginInvoke(3, 4, 3000, cb, null);
Console.WriteLine("Method started");

You might also like