![]() |
General Programming »
Internet / Network »
Remoting
Intermediate
.NET Remoting – Basic ManeuversBy raymond.fung, Norman FungA tutorial on .NET Remoting |
C#, Windows, .NET 1.0, Dev
|
|
Advanced Search |
|
|
|
||||||||||||||||

.NET Remoting is a framework for developing distributed applications. It is the successor to DEC/RPC/DCOM. Simply put, Remoting allows an application (Remoting Client) to instantiate a type (Remotable class) on a remote server (Remoting server) across network. Communication between client and server object (Instance of Remotable class) hosted by a Remoting Server is channeled through a "proxy" � representation of server object on client side.
Remoting Server can be a simple console application, a Windows Service or hosted on IIS. It's responsible for hosting server objects and publishes them to the outside world. A Remoting client is any application that consumes the published server object.
There're many tutorials on this broad subject. Unfortunately, most are lacking, in one way or another, in coverage of basic maneuvers a developer needs to know. The purpose of this article is to supplement MSDN and to provide a complete coverage of basic remoting tasks in one short article.
The tutorial will cover the following topics.
new, Activator.GetObject (server-activated object, or SAO) and Activator.CreateInstance (client activated object, or CAO)
This tutorial does not cover the following topics.
ClientSponsor and sponsoring mechanism [Ref 5]
IAsyncResult, BeginInvoke, EndInvoke, WaitHandle... etc. [Editor Note - hyphens/spaces have been used in the table contents to prevent scrolling]
|
Module |
Folder/Directory |
Primary class |
namespace |
Remarks |
|
Interface Dll |
/CRemote-ObjInterface source: IRemoteObj.cs |
|
|
Interface (
CProfile
To illustrates how to pass custom object between remote object ( CStatusEventSink
This is the event sink class to be instantiated in client's process. It's derived from StatusEventArgs
|
|
Remote Object dll |
/CRemoteObj source: CRemoteObj.cs |
|
|
That's your remote object. This class implement Methods
Properties
With Server- activated- The object ID is to demonstrate remote object lifetime by marking each object with a randomly generated ID. Pay attention to server console when invoking methods on remote object. |
|
Server exe |
/CRemote-ObjServer source: CRemote-ObjServer.cs Executable: \web_vdir\bin\ CRemoteObj-Server.exe (C# console app) |
|
|
Config files
OPTION 2 loads config file as follows:
Please be reminded that security setting in config files will be ignored unless file name comply to the following convention: AssemblyName. exe.config In this case: CRemoteObjServer. exe.config
For this tutorial, rename config file name to: cremoteobjserv.config and place it in the same folder (\web_vdir\bin) as the exe. |
|
Client exe |
/CRemote-ObjClient source: CRemoteObj-Client.cs Executable: \ bin\Debug \CRemoteObj-Client.exe (C# console app) |
|
|
NOTE: If it wasn't due to the fact that we used "new" keyword to instantiate remote object in <BLOCK 2-A>: We could have eliminated reference to Config files (folder: /config file):
In <BLOCK 2-A>, we called Configure to load configuration file:
Please be reminded that security setting in config files will be ignored unless file name comply to the following convention: AssemblyName .exe.config In this case: CRemoteObjClient .exe.config
For this tutorial, rename config file name to: cremoteobjclient.config and place it in the same folder (\web_vdir\bin) as the exe. |
Server and client are both C# console apps. Pay attention to console output.

Before Remoting client and Remoting Server can begin to communicate, two pieces of information must first be properly configured, whether you're on server side or client side:
NOTE: You don't need to specify port number for client side.
Two configuration options:
RemotingConfiguration.Configure("cremoteobjserv.config"); //Configure channel:
ChannelServices.RegisterChannel(httpchannel);
//Register TYPE for server-activated objects (SAO):
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(nsCRemoteObj.CRemoteObj), //Type
"CRemoteObjURI", //"object URI" (NOT URL to remoting server!)
WellKnownObjectMode.SingleCall //Activation mode: SingleCall or Singleton
);
//Register TYPE for client-activated objects (CAO):
RemotingConfiguration.RegisterActivatedType(
Typeof(nsCRemoteObj.CRemoteObj) );Note: There're two types of server object: SAO (server activated object) and CAO (client activated object). SAO can be further divided into two types: singlecall and singleton. Here's a brief description.
SingleCall - A new instance of the remotable class is created every time a client invokes a method through proxy.
Lifetime: Instance remains in memory for duration of method call.
This is stateless.
Singleton - The Singleton Instance is created on first method call.
Lifetime: This instance stays in memory until "Lease" expires.
This is Stateful. Only one instance of Singleton-SAO is instantiated on remoting server. Multiple Remoting Clients are serviced by same Singleton-SAO. Multiple remoting clients can communicate through one instance of Singleton SAO.
Activator.CreateInstance or new keyword. This is in contrast to SAO where server objects are created upon first method call.
Single-call SAO is the most scalable option among the three and is most suitable in a load-balanced environment. Singleton-SAO and CAO offers more flexibility that:

Server activated. File name: cremoteobjserv(wellknown).config

Server-activated. File name: cremoteobjclient(wellknown).config
In addition to type and channel information, configuration file may contain setting such as object lifetime, security... Example: lifetime of Singleton SAO and CAO remote object can be configured in <lifetime> tag in a config file:

Remote object lives as long as CurrentLeaseTime>0. When the object first got instantiated, CurrentLeaseTime = leaseTime. From this point on, CurrentLeaseTime starts decrementing until the next method call, or that a "sponsor" extend the "lease". On next method call (if lease has not yet expired), CurrentLeaseTime is reset to renewOnCallTime. On the other hand, if CurrentLeaseTime decrement to zero before next method call arrives, the object is marked for garbage collection. If this happens, the next method call will be serviced by a new instance of the remote class. In short, "server-activated Singleton" and "client activated" remote object lives for as long as CurrentLeaseTime>0. Lifetime setting can also be configured programmatically:
using System.Runtime.Remoting.LifetimeServices;
LifetimeServices.LeaseTime = TimeSpan.FromMinutes(3);
LifetimeServices.RenewOnCallTime = TimeSpan.FromMinutes(3);
For more information about leasing mechanism and sponsoring mechanism, refer to MSDN under the topic "Lifetime Leases" [Ref 5] and "Remoting Example: Lifetimes" [Ref 8].
new" keyword in BLOCK 2-A, we added reference to remote object assembly (CRemoteObj.dll) in addition to reference to interface assembly (CRemoteObjInterface.dll). In general however, we can retrieve proxy via Activator.GetObject or Activator.CreateInstance, thereby eliminating need to reference remote object assembly (i.e.. Client references ONLY interface assembly).
Basically, client source is separated into two mutually exclusive blocks � marked by the following tags in client's source code CRemoteObjClient.cs:
<BLOCK X-Y>
... code block ...<BLOCK X-Y END>
You should comment out BLOCK 1 when you want to run code in BLOCK 2, and vice versa.
So, depending on setting in CRemoteObjServer, you may wish to comment out one of the blocks before you compile and execute. In addition, <BLOCK 2-A> and <BLOCK 2-B> are mutually exclusive.
Activator.CreateInstance(..) So, again, comment out one of the two before compile and execute.
namespace nsCRemoteObjClient
{ //nsCRemoteObjClient
class CRemoteObjClient
{ //CRemoteObjClient
CRemoteObj.SetupUser()
public string SetupUser(string sname, string spasswd, out string sTime)
public delegate string SetupUserDelegate(...);
[STAThread]
static void Main(string[] args)
{ //Main()
//Scenario 1: "server-activated" remote object.
<BLOCK 1>
try { //try-A<BLOCK 1-A>
Retrieve interface/proxy:
- Illustrates how to retrieve proxy via:
Activator.GetObject(...)- Cast proxy into
IRemoteObj: further separation between provider and supplier.<BLOCK 1-A END>
<BLOCK 1-B>
- LeaseTime
- Invoke method on remote object through interface.
<BLOCK 1-B END>
<BLOCK 1-C> Asynchronous programming/remoting
- Illustrates asynchronous calls on remote objects and how to pass parameters into/out of methods exposed by remote object using
BeginInvoke,EndInvoke.- Illustrates how to pass user defined object to/from remote object.
<BLOCK 1-C END>
} //try-A catch(Exception err) { }<BLOCK 1 END>
<BLOCK 2>
try { //try-B<BLOCK 2-A>
- Illustrates how to configure channel information using config file.
- Illustrates how to instantiate remote object with "
new" keyword.<BLOCK 2-A END>
<BLOCK 2-B>
Illustrates how to retrieve proxy to remote object via
Activator.CreateInstance(...)<BLOCK 2-B END>
<BLOCK 2-C>
- Illustrate how to invoke method through proxy.
- Illustrates how to subscribe to events generated by remote object.
<BLOCK 2-C END>
} //try-B catch(Exception err) { }<BLOCK 2 END>
} //Main()
} //CRemoteObjClient
} //nsCRemoteObjClient
ChannelServices.RegisterChannel
RemotingConfiguration.RegisterWellKnownServiceType
RemotingConfiguration.Configure("cremoteobjserv.config");
Again, you comment out OPTION 1 if you're using OPTION 2, and vice versa.
new", Activator.GetObject (for server-activated) and Activator.CreateInstance (for client activated)
obj.AuthenticateLoser is invoked in <BLOCK 1-B>
objID. Default LeaseTime is 300-sec for Singleton, if time elapsed between method calls is greater than 300sec, a new instance of remote object gets created each call when remote object runs under the following mode:
Lease configuration can be found in server config file:
<lifetime leaseTime="100MS" sponsorshipTimeout="50MS"
renewOnCallTime="100MS" leaseManagerPollTime="10MS" />
In <BLOCK 1-B>, time between consecutive method calls is 101 ms. That's a little over 100 sec. So, every method call should be serviced by a new instance � therefore a different objID. Adjust lease configuration and observe. For server-activated SingleCall, a new instance service every method call.
NOTE: objID is a randomly generated object ID assigned to an instance of remote object CRemoteObj � take a look at constructor.
"new" keyword: Instead of Activator.GetObject or Activator.CreateInstance, we can use "new" to instantiate the remote object. However, if you choose to configure channel and type information via config file, you may instantiate remote object using "new" keyword. However, "new" implies that you'll be instantiating CRemoteObj � as opposed to interface IRemoteObj. This further implies that your client assembly must reference CRemoteObj assembly.
Activator.GetObject: Take a look at CRemoteObjClient.cs <BLOCK 1-A>
IRemoteObj obj = (IRemoteObj) Activator.GetObject(
typeof(IRemoteObj),
"http://localhost:8085/CRemoteObjURI"
);
Activator.CreateInstance: Take a look at CRemoteObjClient.cs <BLOCK 2-B>
//Note that it references CRemoteObjServer � not the remote object.
object[] attrs = { new UrlAttribute(
"tcp://localhost:8086/CRemoteObjServer") };
ObjectHandle handle = (ObjectHandle) Activator.CreateInstance(
"CRemoteObj",
"nsCRemoteObj.CRemoteObj",
attrs);
CRemoteObj obj = (CRemoteObj) handle.Unwrap();
Interface: IRemoteObj (CRemoteObjInterface.cs)
If we instantiate remote object via Activator's static methods, it'd be unnecessary for client assembly to reference remote object's assembly. This provides further separation between the remote object and the client. If client side settings are loaded from config file instead, remote object has to be instantiated using "new" keyword. This implies client has to reference remote object's assembly.
Take a look at the source code. Pay attention to the architecture and assembly references.
User defined class: CProfile (defined in CRemoteObjInterface.cs)
//ATTENTION: Must be marked Serializable to pass
//user-defined object in/out of remote object.
[Serializable]
public class CProfile
{
public string fname;
public string lname;
public string user;
public string passwd;
//... other stuff...
};
Method: CRemoteObj.UpdateProfile (defined in CRemoteObj.cs)
The method takes a CProfile object as argument, and return a CProfile object.
public CProfile UpdateProfile(CProfile p)
{
//...other stuff...
return pout;
}
The method is also exposed by interface IRemoteObj, although it's not necessary. Call to method is trivial. Now, let's take a look at <BLOCK 1-C> in client's source file CRemoteObjClient.cs.
CProfile p = new CProfile("John", "Kennedy", "JFK", "ace");
CProfile p2 = obj.UpdateProfile(p);
Console.WriteLine("UpdateProfile completed. return p2 passwd: {0}",
p2.passwd);
Take a look at <BLOCK 1-C> in client's source file CRemoteObjClient.cs.
//ATTENTION:
//Delegate for CRemoteObj.SetupUser:
public delegate string SetupUserDelegate(
string string1, string string2, out string string3);
//...other stuff...
Main(...)
{
<BLOCK 1-C>
Wrap
SetupUserin a delegate before callingBeginInvoke:
SetupUserDelegate d = new SetupUserDelegate(obj.SetupUser);Invoke
CRemoteObj.SetupUserasynchronously:string sTime; IAsyncResult ar = d.BeginInvoke( "Dexter", "AceInOpen", out sTime, null, null ); //Blocks the current thread until obj.AuthenticateLoser completes. ar.AsyncWaitHandle.WaitOne(); if(ar.IsCompleted) { //Retrieving output: //(a) "output variable" sTime //(b) "return variable" value. string ret = d.EndInvoke(out sTime, ar); Console.WriteLine("obj.SetupUser return. sTime:{0}{1}" + "return value: {2}", sTime, Environment.NewLine, ret); } //...other stuff...<BLOCK 1-C END>
//...other stuff...
}

The event handler is declared in the interface module CRemoteObjInterface.

Routing mechanism: Here's the sequence of things that takes place when client invoke the AuthenticateLoser:

That's it. I hope this article provide a good coverage of most basic maneuvers that would be expected of a component developer. Go through the source code and play around with different configurations. You should be well on your way to building distributed applications and harness power offered by .NET Remoting.
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 6 Oct 2003 Editor: Nishant Sivakumar |
Copyright 2003 by raymond.fung, Norman Fung Everything else Copyright © CodeProject, 1999-2009 Web18 | Advertise on the Code Project |