.Net Remoting Made Easy

The subject of .net remoting can be very complex. There are several different ways you can do remoting; you can marshall by value or by reference, you can use server-side activation or client-side activation. You can choose SingleCall or Singleton instantiation, among others. You can use either of the default protocols (tcp or http) or you can use a custom protocol.

In this article I'm going to attempt to give you, the reader, the most "bang for your buck". In other words, I'm going to use a configuration which I believe addresses the majority of situations you are likely to encounter, and I'm going to strip away everything that's unnecessary for this configuration. (It's sort of an 80/20 thing, if you change 80/20 to about 90/10.)

The scenario we'll look at is one where we marshall by reference, use server-side activation, and Singleton instantiation. The implementation I use will work equally well for either TCP or HTTP protocols. In addition, I'll show you how to make everything configurable so that you can change either the client implementation or the server implementation on the fly, without having to change any code or recompile.

This article is meant to be a quick tutorial to get you up and running quickly. I'm not going to go into any lengthy explanations about what remoting is, or when and why you should use it other than to say that its best used when you need to communicate internally behind your firewall, between applications/servers hosted on the same platform. In other words, you can't use .net remoting to communicate between a .net application and a java application, or over the internet.

If you want more in depth information in that regard, there's a ton of it on Microsoft's web site.

A Little Background
 
Our example will consist of three separate components. We will create these components in Visual Studio 2008 as three separate projects within one solution.

The first of the three projects will be named ServerApp. The ServerApp component will be the actual application containing your business logic, which you want to be able to access remotely.

The second of the three projects will be named Listener. The Listener component will reside with the ServerApp component on the remote server. The Listener component must be running in order for the ClientApp component (see below) to be able to reach the ServerApp component. When the Listener is first started, it loads its App.config file (compiled to Listener.exe.config), and reads from it all the information it needs to tell it how to open the communication channel, which formatter to use (tcp or http), which port to listen on, the object type and assembly name of the ServerApp component, and how to instantiate it. It then opens the channel and listens on the specified port accordingly.

The third project will be named ClientApp. Its App.config file (compiled to ClientApp.exe.config) will contain information telling it the location and port of the Listener component, as well as the formatter to use (tcp or http) and how to instantiate the ServerApp. When the server is called, the ClientApp component uses this information to contact the Listener and request an instance of the ServerApp.

Incidentally, all code listings and references to code are in C#.

Let's Do It
 
So, in Visual Studio, we'll create a new solution called Remoting, and for now let's just create the ServerApp project and the Listener project. (We'll save the ClientApp project for later.)

Creating ServerApp
 
Create a new ClassLibrary project and name it ServerApp. Rename the default class from Class1.cs to ServerClass.cs, then replace any existing code in that file with the following: 
 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.IO;

namespace ServerApp

{

    public class ServerClass : MarshalByRefObject

    {

        public string Capitalize(String asData)

        {

            string lsRet = null; try

            {

                lsRet = "Capitalize returning " + asData.ToUpper()

                    + " at " + DateTime.Now + " from "

                    + Directory.GetCurrentDirectory();

            }

            catch (Exception ex)

            {

                Console.Write(ex.Message);

                Console.Write(ex.StackTrace);

            }

            return lsRet;

        }

    }

}


As you can see, our business logic is pretty simple. Within the ServerApp namespace we create a class named ServerClass. The class inherits from MarshalByRefObject, which it must do in order to be remotely instantiated and marshalled by reference. ServerClass has one method, Captialize, which accepts one string argument. The class then takes that argument, converts it to upper case, embeds it in a string which also contains the date and time and the location of the ServerApp component, and if no exceptions are raised along the way, returns that string.

Creating Listener

Next we will create the Listener component. In Visual Studio, within the same Remoting solution in which we created the ServerApp project, create another new project. This one will be a Windows Forms Application, and we can name it Listener.

Rename Form1.cs to FormListener.cs, and add a button to the form. Name the button btnStop, and assign "Stop Server" as its Text property. Double click anywhere on the form to load the code behind file. In the FormListener_Load event, copy the following code:

private void FormListener_Load(object sender, EventArgs e)

 {

     try

     {

         RemotingConfiguration.Configure(

             Assembly.GetExecutingAssembly().GetName().Name + ".exe.config", false);

     }

     catch (Exception ex)

     {

         Console.WriteLine("ERROR - " + DateTime.Now + "... " + ex.ToString());

     }

}

 
Examining the above code, we can see that the RemotingConfiguration.Configure() method loads all the configuration information from your App.config file (we'll get to that shortly), and loads ServerApp accordingly. The first argument being passed to it represent the name of the config file to load, in this case "Listener.exe.config". (Your App.config file will take this name when your application is built.) The second argument is a boolean argument that tells the method whether to implement security or not. For our purposes we will not, so we pass false. 

In order for this to compile, you will also need to add the following using statements:


using System.Runtime.Remoting;

using System.Reflection;

 
Next, double click on the Stop Server button so that the btnStop_Click event is displayed. Then copy the following code into the event: 
 

private void btnStopTCP_Click(object sender, EventArgs e)

{

    Application.Exit();

}


Next, we need to add the App.config file. In order for it to work properly, it needs to be created not as a regular text file, but as follows: 

  • Right-click on the Listener project
  • Click Add
  • Click New Item
  • Choose Application Configure File from the dialog box, making sure that the file is named App.config.
Open the newly created App.config file, copy the following code into it, and then save it:
 

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.runtime.remoting>

    <application>

      <service>

        <wellknown mode="Singleton"

            type="ServerApp.ServerClass, ServerApp"

            objectUri="ServerClass">

        </wellknown>

      </service>

      <channels>

        <channel ref="tcp" port="1234" />

      </channels>

    </application>

  </system.runtime.remoting>

</configuration>


Note that the channel element has a ref attribute and a port attribute. As illustrated, the settings will cause the Listener to listen for a tcp request on port 1234. If you wanted to use http instead, you would just change the ref attribute to "http".

Finally, build the entire solution. Once everything is built, create a new directory on your hard drive called "C:\RemoteTest". Into this directory you will need to copy the following three files from your build:
  • Listener.exe
  • Listener.exe.config
  • ServerApp.dll
Creating ClientApp

Now we're going to create one more Windows Forms Application project in our Remoting solution, called ClientApp. Next, we want to add a reference to the ServerApp application. Right-click the newly created ClientApp project, click Add Reference, and in the Add Reference dialog box, click the Browse tab. Browse to "C:\RemoteTest\ServerApp.dll", and choose it.

Note: Be sure to reference this dll, and not the ServerApp project.

Next, rename Form1.cs to FormClient.cs. Then add a text box and two buttons. Name the text box txtData, and name the two buttons btnSubmit and btnExit. Open the code-behind file and add the following using statements:
 

using ServerApp;

using System.Runtime.Remoting;

using System.Reflection;


Next, add the following class level variables:
 

ServerClass server;
string result;


Next, double-click anywhere on the form so that the FormClient_Load event is displayed. Then copy the following code into that event:
 

private void FormClient_Load(object sender, EventArgs e)

{

    try

    {

        RemotingConfiguration.Configure(

            Assembly.GetExecutingAssembly().GetName().Name +

".exe.config", false);

 

        MessageBox.Show("Channel created and client registered.");

 

        server = new ServerClass();

    }

    catch (Exception ex)

    {

        MessageBox.Show("Unable to connect to remote object.",

                   "Error",

                MessageBoxButtons.OK, MessageBoxIcon.Error);

        Console.WriteLine("Message " + ex.Message);

 

        Console.WriteLine(ex.StackTrace);

    }}

 
Looking at the above code, we can see that the RemotingConfiguration.Configure() method loads the ClientApp's App.config file, known as ClientApp.exe.config after the build. If successful, an instance of ServerClass is instantiated.

Next, double-click on the btnSubmit button and copy the following code into the btnSubmit_Click event:
 

private void btnSubmit_Click(object sender, EventArgs e)

{

    try

    {

        result = server.Capitalize(txtData.Text);

 

        MessageBox.Show("Server returned: '" + result + "'.", "SUCCESS",

            MessageBoxButtons.OK, MessageBoxIcon.Information);

 

    }

    catch (Exception ex)

    {

        MessageBox.Show("Error Occured: " + ex.Message, "Error",

                         MessageBoxButtons.OK, MessageBoxIcon.Error);

 

        Console.WriteLine(ex.ToString());

    }

}


Next, double-click on the btnExit control so that the btnExit_Click event is displayed, and copy the following code into it:
 

private void btnExit_Click(object sender, EventArgs e)

{

    Application.Exit();

}


Next, we need to add the client's App.config file. In order for it to work properly, it needs to be created not as a regular text file, but as follows:
  • Right-click on the ClientApp project
  • Click Add
  • Click New Item
  • Choose Application Configure File from the dialog box, making sure that the file is named App.config.
Open the newly created App.config file, copy the following code into it, and then save it: 
 

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.runtime.remoting>

    <application name="ClientApp">

      <client>

        <wellknown mode="Singleton"

            type="ServerApp.ServerClass, ServerApp"

            url="tcp://localhost:1234/ServerClass">

        </wellknown>

      </client>

      <channels>

        <channel ref="tcp" />

      </channels>

    </application>

  </system.runtime.remoting>

</configuration>


Note that the client side config file is slightly different from the server side config file. This is the way it should be. When we test the client, the url in the config file will point to the server localhost. If you eventually install the ServerApp and Listener components on a different machine, you will need to change the server name accordingly so that it points to that machine.

Note also that if you wanted to use http instead of tcp, you would need to change the channel element's ref attribute, and the wellknown element's url attribute accordingly.

Now you can build your ClientApp project.

Putting It All Together

In order to test all of our components, perform the following steps:
  • Close Visual Studio.
  • Go to C:\RemoteTest, and run Listener.exe
  • Run your ClientApp.exe
  • Enter any text into the ClientApp's text field
  • Click the Submit button
You should see a message box containing the string returned from the server, which should include an upper case version of the text string you entered, the date and time, and the directory in which the ServerApp component resides.

I hope that this article sheds some light on an otherwise sometimes convoluted topic. Microsoft's web site contains much more information on .net remoting if you want to find out more.

Dave Verschleiser
Murray Hill Technologies