Abstract
Remoting is an infrastructure that allows the developer to use remote objects. Remote objects are objects that are based (or instantiated) outside of the caller's Application Domain. This example shows you how to use both remote object access mechanisms (pass by value and pass by reference). It also shows you the power of remoting for distributed computing with a simple yet powerful implementation of a Task Server.
The Task Server can take any object that implements the simple ITask interface, and run it within its own Application Domain. What's more, it can accept tasks from multiple clients at one time.
This tutorial requires that you have had exposure to C#. It does not require that you have used C# Remoting.
At the end of the tutorial you will be able to:
- Set up a client / server object relationship
- Pass an object by value
- Pass an object by reference
- Understand the concepts of remote task distribution
Remote Objects
Remote objects are either passed by reference or passed as an entire object.
In the first instance, the object's reference is passed from App Domain 'A' to App Domain 'B', but the object method calls are marshalled between 'A' and 'B'. The object lives and operated on App Domain 'A', but appears to App Domain 'B' to be a locally instantiated object.
In the second instance, the entire object and it's dependencies (known as the Object Graph or Web) is serialized into byte-form and literally delivered from App Domain 'A' to App Domain 'B'. The object is then unserialized and brought to life on App Domain 'B'. The object now lives and operates locally on App Domain 'B'.
Two areas need to be attended to, namely the server Application Domain (i.e. the object host) and the client (i.e. the object client).
Setting up the Object Host, known as the Server
The first step to be taken when setting up the server is to create a Channel through which your object will communicate. This can be either a TCP/IP channel or an HTTP channel. TCP channels are faster and recommended for networks that have limited firewall restrictions on packets traversing through networks. HTTP channels, however, are far more flexible and are recommended for environments where remoting is done over broad networks such as the Internet.
We will be using a TCP/IP channel, and will run the client and the server on the same machine, under two different application domains. Thus, to create my channel on port 8065 on my local TCP/IP stack I type the following code:
TcpChannel myChannel = new TcpChannel(8065);
The next step is to register the channel with .NET's Channel Services. This will make the channel publicly accessable outside of the server's app domain. To do this I type:
ChannelServices.RegisterChannel(myChannel);
The last step is to tell the .NET remoting infrastructure about the object that we want to make public. We need to give the object type and location, a name by which clients will locate the object and the way in which the remoting infrastructure must handle calls made to the object. To get the object type I do the following (rather unconventional, but makes more sense to me):
Type objectType = new MyCoolObject().GetType()
To register the object with the .NET remoting infrastructure I type the following:
RemotingConfiguration.RegisterWellKnownServiceType(
objectType, "MyCoolObject",
WellKnownObjectMode.Singleton);
There are two ways an object call can be handled: Singleton and SingleCall. Singleton implies that the object is created on the first client method call and kept alive until the client breaks the connection or the object exits naturally. SingleCall implies that the object is created on every client method call, is alive for the duration of the method call and then exits once the method finishes. The client connection does not end with a SingleCall, only the object is destroyed.
Setting up the client for the remote object
The first requirement on the client side is that the compiled class for the remote object is available locally. This is used by the .NET remoting infrastructure proxy to intercept and marshal messages being passed to and from the remote object.
A channel needs to be created once again, and then registered with the infrastructure so that it can become useable:
TcpChannel myChannel = new TcpChannel();
ChannelServices.RegisterChannel(myChannel);
Note that we don't specify a port address when we create the channel. This is specified in the following step, when we ask the server to give us a reference to the remote object that we are after. The code:
MyCoolObject mine = (MyCoolObject)Activator.GetObject(
typeof(MyCoolObject),
"tcp://localhost:8085/MyCoolObject");
The first parameter gets the type of the object we are trying to locate. The second parameter specifies the URL of the remote object. Once we get the object we need to type-cast it to a MyCoolObject from a generic object.
We now have a reference to the live object on the server, which we can use as if it were within our local app domain.
The All Powerful Task Server
The Task Server is an explicit example of how one can use .NET's pass-by-value object remoting mechanisms. The Task Server has an object called the TaskRunner which a client can get a reference to. The TaskRunner is designed to take in any object from the client that implements the ITask interface.
The ITask interface enforces that clients create their task objects with the methods Run() and Identify(). Clients can then create a complex and resource-intensive task that implements the ITask interface and submit it to the Task Server for execution within his application domain. This means that clients that do not have adequet computational resources to execute their complex task can make use of the Task Server's massive resource pool to do the work for them.
So, a client who would like to work out Pi with 3000 decimal accuracy can create an object that performs the calculation and implements the ITask interface. The task is then submitted using the referenced TaskRunner object to the Task Server for execution.
The ITask interface enforces that clients return an object from their Run() method. This can, ofcourse, be a null value � but it also allows for the client to return an intelligent encapsulated result.
In a nutshell, remoting using pass-by-value is best for situations where the server does not have an explicit representation of the object that the client would like to execute in a remote context.
ass="smallblack">Passing Objects by Value
When an object is to be passed by value, it needs to be converted into a transportable form. A live object instantiated within an application domain takes up a piece of system memory and is able to respond to messages send to it from other objects. To send this object to another application domain the object must be serialized. This means that it must be broken down into a collection of bytes that can be used to create a clone of the object within the destination app domain.
To do this, you will use the Serializable compiler attribute. This instructs the compiler to serialize the following construct. You can flag an entire class as Serializable, or only parts of it including a selected number of methods. In the Task Server environment, the entire client task object must be Serializable. This is done as follows:
Using System;
�
[Serializable()]
class ClientTask {
�
Note that if an object has dependencies, such as an outside library, that is used by the object then the parts of the library that are used must also be serializable. This is a direct implication of the serialization process and is required to build up the object in the remote app domain. These dependencies are referred to as the object graph.
Running the Example
This example has been written and tested in C# for .NET Beta 2
To use the Task Server, you must compile the code in the following order
csc /target:library /out:ITask.dll ITask.cs
csc /target:library /out:TaskRunner.dll TaskRunner.cs
csc /r:ITask.dll /r:TaskRunner.dll TaskServer.cs
csc /r:ITask.dll /r:TaskRunner.dll TaskClient.cs
The order is relevant because the TaskServer and TaskClient classes are dependent on the TaskRunner and ITask libraries.
To run the example, first start the server and wait until you see:
[i] Task Server Started, press <enter> to exit� </enter>
Then you can run the client application.
Inside the TaskClient you will find a class that implements the ITask interface and is serializable. This task will multiply two numbers together and return an 'int' object. I encourage you to create your own task and try running it on the Task Server. This will give you a first-hand feel for the power of this form of Distributed Computing. It essentially allows client developers to make use of the abundant resources available on the Task Server's machine to perform complex and intensive tasks.
You can use the Task Server to perform operations like number-crunching, compression, encryption, sorting � the list is endless.
The Source Code
ITask.cs – The ITask Interface
TaskRunner.cs – The Task Runner Library
TaskServer.cs – The Task Server
TaskClient.cs – The Client Application
Remoting-Source.zip – All the source files zipped

