If you have ever worked with DCOM or CORBA, then you have an
understanding of how to work with Remote Objects and how to deal with
application communications across boundaries. .NET Remoting is
Microsoft’s new infrastructure that provides a rich set of classes that allow
developers to ignore most of the complexities of deploying and managing remote
objects. In .NET Remoting, calling methods on remote objects is nearly identical to
calling local methods.
Remoting is a framework built into the common language runtime (CLR) that can be used to build sophisticated distributed
applications and network services. When a client creates an instance of a remote
object, it receives a Proxy to the class instance on
the server. All methods called on the Proxy will
automatically be forwarded to the remote class and any results will be returned to
the client. From the client's perspective, this process is no different than making a
To use .NET remoting to build an application in which two components communicate
directly across an application domain boundary, you need to build only the following:
A remotable object.
A host application domain to listen for requests for that object.
A client application domain that makes requests for that object.
Even in a complex, multiclient/multiserver application, .NET remoting can be thought
of in this way. The host and the client application must also be configured with the
remoting infrastructure and you must understand the lifetime and activation issues
that the remoting infrastructure introduces.
Proxy objects. When a client creates an instance of a remote object,
it receives a proxy to the class instance on the server. All methods called
on the proxy will automatically be forwarded to the remote class and any
results will be returned to the client. From the client's perspective, this
process is no different than making a local call. Any exceptions thrown by
the remote object will automatically be returned to the client. This enables
the client to use normal try and catch blocks around sections of the code to
trap and deal with exceptions.
Object passing. All objects created remotely are returned by reference
and have to derive from MarshallByRefObject. Objects passed as
parameters to a remote method call can be forwarded by value or by reference.
The default behavior is pass by value provided the object in question is
marked by the custom attribute [serializable]. Additionally, the object could
implement the ISerializable interface, which provides flexibility in
how the object should be serialized and deserialized. Objects that are not
marshal by reference or marshal by value are not remotable.
Activation models. Remote objects can easily be created from a client
by calling new. The framework contains enough "intelligence" to
realize you are dealing with a remote object and will ensure an instance of
the object gets created in the relevant remote application. Creating
instances of remote objects is not limited to default constructors; you can
even do this using a constructor that requires one or more parameters. The
Activator class contains two methods, CreateInstance and
GetObject, that can also be used to create an instance of remote
objects. The former can be used in place of new to create an object instance
while the latter is normally used to connect to an object at a specified URL.
Stateless and Stateful objects. The framework makes a provision for
creating remote objects as stateless. When an object is configured as
SingleCall, it will be created when a method is called on that object.
The object processes the call, returns an optional result, and is then
collected by the garbage collector. This way the client is always connected
to a fresh object with each call. Configuring an object as a Singleton
ensures that all clients will be connected to the same object whenever a call
is made to that object. ClientActivated objects allow the client to
pass parameters to the constructor of a remote object when it gets created.
Each activation request for a client activated object
(Activator.CreateInstance or new in combination with entries in the
configuration file) on the client results in a new object on the server.
Channels and Serialization. When a client calls a method on a remote
object, the remoting framework automatically serializes any data associated
with the request and transports the data to the remote object using a
channel. Some of the more popular channels supported are HTTP, TCP, and SMTP.
In the case of HTTP, the framework uses the SOAP protocol to transport
data in XML format from the client to the server and back. The default
serialization formatter for HTTP is a SOAP formatter. Since programmers can
create custom formatters for use with any channel, the remoting framework can
be configured to work with any external .NET Framework on other platforms.
The TCP channel uses plain sockets and Binary Serialization by default and
can be used to communicate with any object on a remote server.
The following example, we will show how to transfer DataSets
between different computers. DataSets were built
to work with .NET remoting capabilities. Not only are DataSets
designed to be marshaled accross boundaries, but they also have capabilities
specifically designed to make multi-tier development more efficient.
Creating the Server
This class needs to be in a DLL that can be called by the server
and can also be referenced by the client (so that the client knows what methods are
available to call). We create a class library called «MyServer», using
Visual Studio. We then create a new class called «RemoteServer». It is
important that the «RemoteServer» class is inherited from
MarshalByRefObject which tells .NET how this class should be transfered over
the network. An object can be passed from one place to another in two ways. When you
pass something like a number or a string, you usually only want to pass the
value, this is called marshaling by value. The other way of passing an
object is by reference. Instead of just copying the data within an object, a
reference to the object is passed. The receiver of the object can then call methods
on the object directly. This is how most objects in C# are passed - if you pass a
DataTable to a method, you are passing a reference to that DataTable, not making a
copy of it. Behind the scenes, .NET automatically builds the proxy object to
represent the object on the other machine you are calling.
// This class represents the Server Object,
which can be
// accessed remotly using the Proxy Object.
public class RemoteServer : MarshalByRefObject
// Method to be
called by client that returns a DataSet
// across the wire.
public DataSet GetDataSet()
DataSet ds =
conn = new SqlConnection(strConnect);
string strSQL = "SELECT CustomerID,ContactName FROM Customers";
SqlDataAdapter sda = new SqlDataAdapter(strSQL,conn);
ds = new DataSet();
Hosting the Server Object
We now have an object designed to be called from remote, it is
however not enough. We cannot just compile the code and wait for .NET to find it. For
our purposes, though, we will build a small server that sits on the computer
Focus and waits for requests. Our server has two parts: a small console
application and a configuration file. Lets look at the application first.
We create a new console application called «TestApp». Once the
application is created, we add a reference to the «MyServer» DLL, so that
the server can create the appropriate object - the «RemoteServer».
Add a Reference to the «MyServer» DLL
from the Test Application «TestApp»
// Set up a fully functional server
listening for requests.
// It is important that the TestApp has a reference to the
// assembly containing the server code (MyServer). The Server
// can be tested with thw following URL from any Web-Browser
static void Main(string args)
// Get the Application Directory
// Use the Configuration File to configure and start the Server
strConfigFile = strAppDir + "\\TestApp.exe.Config";
// Keep the Server alive to respond to
Console.WriteLine("Server running and waiting for requests ...");
Console.WriteLine("Press [ENTER] to shut down Server");
The Configuration File
The Configuration File contains the heart of what the server is
supposed to do. The configuration file's name follows the remoting convention - the
executable name followed by .Config - TextApp.exe.Config.
<channel ref="http" port="8086"/>
- The Name of the application that will be exposed.
(2) - The
first attribute mode indicates how the object
should be accessed, the activation model
There are 2 activation models for Remote Objects:
The Server Side Activation (you can also call
this method “Well Known” activation)
is when the Remote object is created and executed totally on
the server side while the client creates a proxy to trick it into thinking that
the Object is available on the client side. Even in this activation model we have
2 separate ways to deal with the state fullness of the Object:
The SingleCall flag notifies the server that each remote
method call into the server will create a new instance of the object on the
server which means there will be no state kept for that object on the server
between method calls.
The Singleton flag notifies the server that remote method
calls into the server do not destroy the instance of the remote object on the
server after the method returns which means subsequent calls from the client
can take advantage of the state of the object made from previous
The second activation model is the Client Side Activation, and
that is when the client is allowed to pass parameters to the constructor of a
remote object when it gets created. The trick here is that the Object in question
has to have the [Serializable] attribute set.
As you can see in the example, the object is a singleton - we
want to have only one server running.
The next attribute, type,
identifies the type of object to create. The first part of the argument (before the
comma) is the classname (RemoteServer), along with
its namespace (Akadia.MyServer). The second part of
the argument (after the comma) is the name of the assembly that contains the class.
In this example, the assembly and the DLL are one and the same, so the assembly has
the same name as the DLL (MyServer).
The objectURI specifies
how the object will be referenced, this value will become part of the URL, that
will be used to find and communicate with the server (http://focus:8086/MyServer/RemoteServer.soap)
(3) - This
information says that the server will be available via HTTP, using port 8086.
Testing the Server
Although we do not yet have a client, we now have everything we
need to run the server. Of course, we should test to make sure the server is working
correctly. Fortunately, we habe a generic client we can use that already knows how to
talk HTTP, the web browser.
The remoting services automatically create a special call for
describing the interface of a remote application. The format for the description is
called WSDL (Web Service Description Language), which is XML based. You
can access it directly from a web browser - of course you must start the server
Start the Server:
Test the Server from any web browser:
Although you can go through the output to determine information
about the server, the important thing is taht, by getting any response back, we know
the server is running, click here for the full
Creating the client
Now that we have proven that the server is working, the last step
is to build a client application to talk to the server. We do this with yet another
console application. It needs to have a reference to the MyServer DLL so that it
knows what methods are available.
Add a Reference to the «MyServer» and
System.Runtime.Remoting DLL from the Client Application
// You must add a reference to MyServer,
and to System.Runtime.Remoting
static void Main(string args)
// Register a channel
= new HttpChannel(8088);
// Get a proxy to the server object
RemoteServer dss =
// Call the method on the proxy
DataSet ds = dss.GetDataSet();
// Write out the results
Console.WriteLine("\r\nPress enter to exit");
// Writes out the
public static void
dr in dt.Rows)
// Step through each column in the row and write it
foreach(DataColumn dc in dt.Columns)
if (dc.Ordinal > 0)
if (dr.RowState == DataRowState.Deleted)
// For Deleted rows, need to access the original
// Otherwise, just write out the regular version
There are a bunch of namespaces here. Channels contains classes
for setting up a channel - a channel is used to
transport messages. The Http namespace has the
specific channel capabilities for using HTTP, as you would expect.
HttpChannel ch = new HttpChannel(8088);
This code creates a new HTTP channel and registers it for use.
The argument to the HttpChannel's constructor is the port on which communication
should occur. You may have noticed that this port is not the same as the one we
used for the server. That difference is deliberate - messages go out on one port
and return on another. You cannot use the same port for both sides on the same
machine, although you can use the same port on multiple machines - therefor we used
RemoteServer dss =
This is the real magic line. The call to the Activator GetObject() method creates a proxy for a currently
running remote object, server-activated well-known object, or XML Web service. It
really just setup to call the object remotely. The information we are providing
includes the type of the object we want and the location of the real object. The
Activator does the rest for us. We must typecast the result to be a RemoteServer,
though, because GetObject() returns a regular
One interesting point here is - even though this code has
created the proxy for us, it has in no way connected to the server or confirmed
that the real object exists. Doing so woulkd be inefficient, because it would
require a round-trip. The connection won't be tested until we call a method on the
DataSet ds = dss.GetDataSet();
Even though we know the reference returned to us is not the
RemoteServer, but is, in fact, a proxy, we can treat it as though it really were
the object. The code simply calls the methods on the object as though it were local
In this case case, the server is asked to generate a DataSet and
return it. The client, with no code of its own create a DataSet, nonetheless is
handed one. This code would work if there were no way for the client to talk to the
database, because the client never talks to the database. The PrintDataTable() method simply prints out the DataSet content.
Testing the Client
After all, we have to install the client code on any machine.
First you have to install the Microsoft .NET Framework Version 1.1 Redistributable
Package. The package includes everything you need to run applications developed
using the .NET Framework. You can download it from
Microsofts Download Site.
After all that, the output from the client is not very exiting -
it simply shows the output from the table Customers on one of our SQL-Servers.
However, the example doesn't matter as much as the power behind the concepts. With
very little code is was possible to create a distributed application.