Marshal Objects ByRef Over The Internet With .NET Remoting

By Robbe D. Morris

Printer Friendly Version

Robbe Morris
Robbe & Melisa Morris
What does marshal objects by reference over the internet really mean?  That is the first question I asked when I began to look into .NET Remoting.  Truth is, it means exactly what it says.  In the same way we've grown accustomed to passing objects between methods in our code via COM, we can now perform that same task with two different applications communicating over the network.  There are a variety of ways to use .NET Remoting.  Today, we're going to look specifically at marshaling objects by reference with Well-Known objects either as Singleton or Single-Call.  We'll discuss potential uses along with the source code example below that demonstrates how and when communication takes place between the client and server applications.
 


 
How does it work?
In simplistic terms, remoting in this scenario happens by proxy.  The developer must create an interface class containing all properties and methods minus the actual code that will reside in each.  The interface class must exist on both the client and server.  Remember, this is only the public interface definition.  The client will utilize the interface class to instantiate a proxy to the object locally.  Via the proxy, anytime a property is referenced with a Get or Set statement or a method is called, .NET handles the communication to and from the client to the server over an http channel and the port designated by the developer.
The actual code will reside in another class on the server that implements that same interface class.  As long as the public interface isn't redefined, you are free to change the code on the server without having to redistribute any code to the client machines.  As you would expect, the server and client applications need to have a project reference included to the interface class as well as the System.Runtime.Remoting class.  You'll see an example of this in the source code below labeled: NullSkull_RemoteServer Source Code.
 
What is the difference between Well-Known and Client-Activiated server objects?
There is no permanent or constant connection maintained with Well-Known objects.  In much the same way a browser is disconnected from the web server, well-known objects are connected to, worked with, and then disconnected.  Client-Activated objects maintain a connection until their task is complete.  Similar in concept to a TCP/IP socket connection application works (some TCP/IP apps choose to work in a disconnected environment).  Well-Known objects come in two different types: Singleton and Single-Call.  Your production application may end up using both depending on your requirements
 
What is the difference between Singleton and Single-Call well-known objects?
A well-known singleton object is created by the first user to access it and is used, shared, and maintained in memory until the server service/application shuts down.  So, if you use properties in your class, their values will be shared across all client connections that access them.  Similar in nature to a public static variable.  In the code sample below, you have the option to watch the process in use with a singleton object .vs a single-call object.  Notice that when you launch multiple instances of the client application, the server class constructor only fires once.  Well-known single-call objects are quite different.  Every client access triggers the server class constructor.  Thus, making properties obsolete because the object is not held in memory server side in between references from the same client.  Single-call objects are ideal for classes that hold methods across a server farm.
 
How can I use .NET Remoting?
That, of course, is a loaded question.  Your initial thoughts of putting much of your business logic in a web farm dedicated to remote objects is probably the most common use for .NET Remoting.  However, I think there are quite a few other instances where .NET Remoting might be a good fit.  Here are a couple of other potential uses:
An additional level of software/data security can be achieved with .NET Remoting.  For instance, if your users work in a highly secure environment with classified data, it might be useful to not to put the business rules on the desktop.  Doing so, makes it tougher to take the software out of the office for inapproriate use.  With .NET Remoting, all of the core business rules and functionality could exist on the server making the software on the desktop useless for reverse engineering purposes.  Granted, most business environments don't require this level of security.  However, for those that do, this could provide just one more tool in their security arsenal.
As many of you know, I work for an IT research and analysis firm building custom software and websites.  Intellectual capital is extremely valuable to our organization.  Our research data and the analysis frameworks behind it go hand in hand.  Over the next year or two, we'll undoubtedly begin exploring ways to sell access to our remote objects that can bind our research data around the frameworks for use in applications developed by other major IT companies.  This will allow these companies to get the best of both worlds: access to our data and analysis while still being able to wrap these in custom built applications.  We've also discussed and used web services but they don't always provide the power and level of capabilities that a .NET Remoting solution would.
 
Summarization
Now, that you've got a reasonably good idea of how .NET Remoting works and how it might be used, take some time to play around with the test client/server application I've put together.  It is a pretty basic test app designed specifically to show how and when the communication between the client object and server object take place.  It is crucial that you get a good feel for how this works and performance will be impacted.  You'll be able to see how Singleton and Single-Call objects act differently and the pluses and minuses between the two.  If you like, feel free to download the complete set of all three solutions in this zip file: Download Source Code.  For best results, compile the server and client solutions into executables.  Then, create shortcuts on your desktop for each.  This will make it easier to view the console windows for the server instance and client instance(s).
For a more indepth discussion of .NET Remoting, check out Jesse Liberty's book Programming C#.

NullSkull_RemoteBusinessRules Interface Definition Source Code
 using System;

namespace NullSkull_RemoteBusinessRules
{
	 
   public interface iMathClass
   {
     string ClassName 
     {
       get; 
       set; 
     }

     double CalculateValues(int nVal1,int nVal2);

   }
}

NullSkull_RemoteServer Source Code
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;

namespace NullSkull_RemoteServer
{
	
   // Create a class implementing the interface class NullSkull_RemoteBusinessRules.iMathClass
  // Also, set our class to enable it to be marshaled
  

    public class BusinessRules : MarshalByRefObject,NullSkull_RemoteBusinessRules.iMathClass 
    {
        private string msClassName;

        public BusinessRules()
        {
             Console.WriteLine("Server Business Rules Constructor");
        }

        public string ClassName 
        {
            get 
            {
                Console.WriteLine("Server Get Class Name: " + msClassName);
                return msClassName; 
            }
            set 
            {
               msClassName = value;
               Console.WriteLine("Server Set Class Name: " + msClassName);
            }
        }

        public double CalculateValues(int nVal1,int nVal2)
        {
            double dRetVal=0;
            dRetVal = nVal1 + nVal2;
            Console.WriteLine("Server Calculate Values: " + dRetVal.ToString());
            return dRetVal;
        }

    }

    public class RemoteServer_Listener
    {
        public static void Main()
        {
      
            try
            {
             
             Console.WriteLine("Remote Server Process Waiting For Client Connections.  Press Enter To Quit");

             // Pick a dynamic or private port to listen on.

             HttpChannel oChannel = new HttpChannel(65100);
             
             // Register the channel

             ChannelServices.RegisterChannel(oChannel);

             // Create a Type object for use as a parameter to the RemotingConfiguration Registration method of your choice.
             Type oCalcType = Type.GetType("NullSkull_RemoteServer.BusinessRules");

             // Make this server process run as a Singleton object.
             RemotingConfiguration.RegisterWellKnownServiceType(
                   oCalcType,"NullSkull_RemoteServerEndPoint",WellKnownObjectMode.Singleton);  
            
             // Make this server process runs as a Single-Call object
         //    RemotingConfiguration.RegisterWellKnownServiceType(
         //       oCalcType,"NullSkull_RemoteServerEndPoint",WellKnownObjectMode.SingleCall);  
           
            }
            catch(Exception e) { Console.WriteLine(e.Message);  }
             Console.ReadLine();
        }
    }
}

NullSkull_RemoteClient Source Code
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;

namespace NullSkull_RemoteClient
{
 
    public class BusinessRulesClient
    {
        public static void Main()
        {
             double dRetVal;

             Console.Write("RemoteClient Main Starting Up\n\n");
             
            try
            {
                
                // Use 0 as the port to indicate that we are not listening on this channel
                HttpChannel oChannel = new HttpChannel(0);
             
                // Register the channel
                ChannelServices.RegisterChannel(oChannel);

                // Get an instance of the remote class over the network
                MarshalByRefObject oRemoteInterface = (MarshalByRefObject) RemotingServices.Connect(
                            typeof(NullSkull_RemoteBusinessRules.iMathClass),
                            "http://localhost:65100/NullSkull_RemoteServerEndPoint");

                // cast the remote class to our local interface class

                NullSkull_RemoteBusinessRules.iMathClass  oRemoteBusinessRules =
                             oRemoteInterface  as NullSkull_RemoteBusinessRules.iMathClass;
                
                // Use the class and watch the server console window after each time we hit enter

                Console.WriteLine("Press Enter To Continue: " + oRemoteBusinessRules.ClassName);
                Console.ReadLine();

                dRetVal = oRemoteBusinessRules.CalculateValues(2,2);
 
                Console.WriteLine("Client Calculate Value: " + dRetVal.ToString());
                
                Console.WriteLine("Press Enter To Continue: " + oRemoteBusinessRules.ClassName);
                Console.ReadLine();

                oRemoteBusinessRules.ClassName = "Test String Value";

                Console.WriteLine("Client ClassName Get: " + oRemoteBusinessRules.ClassName); 

                oRemoteBusinessRules.ClassName = "Test String Value";
                
                Console.WriteLine("Press Enter To Quit");
                
            }
            catch(Exception e)
            {
                Console.WriteLine("Client Main Error: " + e.Message);
            }
           Console.ReadLine();
        }
    }
	 
}


Robbe has been a Microsoft MVP in C# since 2004.  He is also the co-founder of NullSkull.com which provides .NET articles, book reviews, software reviews, and software download and purchase advice.