Google
WWW Yariv Hammer's Code Site

Friday, October 21, 2005

Creating a Simple Server/Client Application With Remoting - A Step-By-Step Tutorial

In my previous post I explained some basic concepts regarding Remoting.NET. In this post I will set up a small example.
The demonstartion works for VS.NET 2003 using C#. I designed the sample to be very abstract in order for anyone to apply it to their best interest.

Step 1 - Creating the solution.
Create a blank solution, and add three projects to it: Server, Client (both Windows Applications), and Common (a class library - our shared assembly). Add a reference to Common from Server and from Client.

Step2 - Creating the interfaces.
Add to Common a file, and call it interfaces.cs .
The code is here:
------------------------------------

namespace Common
{
   public interface IObjectFactory
   {
      IShareableObject GetObject();
   }

   public interface IShareableObject
   {
      long X { get; }
      string Message {get;}
   }
}

----------------------------------------------
IObjectFactory is the interface for our Remote object. The client will know this interface and not the implementation of the object - It will be marshaled by reference
IShareableObject is the interface for the object we want to pass around the processes (In our case the client will get it from the server) - It will be marshaled by value.

Step 3 - Create the Serializable object.
Add to Common project a class called ShareableObject which implements IShareableObject. This is just an example of course.
---------------------------------------

using System;
namespace Common
{
   [Serializable()]
   public class ShareableObject:IShareableObject
   {
      private long _x;
      private string _msg;

      public ShareableObject(long x, string msg)
      {
         _x = x;
         _msg = msg;
      }
      public long X
      {
         get { return _x; }
      }
      public string Message
      {
         get { return _msg; }
      }
   }
}

--------------------------------------
Notice two things: The class is in the Common assembly meaning that the implementation will be familiar to both the client and the server; The class is tagged with the Serializable attribute, meaning it will be passed around processes.

Step 4: Create the Server
Add an object to the server called ObjectFactory which implements IObjectFactory. It should inherit from MarshalByRefObject:
--------------------------------------

using System;
using Common;
namespace Server
{
   public class ObjectFactory: MarshalByRefObject,IObjectFactory
   {
      public IShareableObject GetObject()
      {
         return new ShareableObject(10,"123");
      }
   }
}

--------------------------------------
Notice how we instanciate an object of type ShareableObject (From Step 3). It will be passed by value to the client. When the client will access ObjectFactory it will call the instance in the server through a proxy. When calling GetObject it will get the object and will be able to call its method and properties from the client's process.
Also notice how the method GetObject returns an interface, but creates some concrete class.

Add a new item to the Server project which is Aplication Configurion file and make it look like this:
--------------------------------------

<?xml version="1.0" encoding="utf-8">
   <configuration>
      <system.runtime.remoting>
         <application name="MyServer">
            <service>
               <wellknown mode="Singleton" type="Server.ObjectFactory, Server" objectUri="ObjectFactory.soap"/>
            </service>
            <channels>
               <channel ref="tcp" port="16784">
                  <serverProviders>
                     <formatter ref="binary" typeFilterLevel="Full"/>
                  </serverProviders>
                  <clientProviders>
                     <formatter ref="binary"/>
                  </clientProviders>
               </channel>
         </channels>
      </application>
   </system.runtime.remoting>
</configuration>

-----------------------------------------------
This Xml defines the Remoting configuration. We will read the file soon, but let me focus on some issues:
- The ApplicationName will be seen again in the client.
- We registered the ObjectFactory as a singleton. Without it the client will nbot be able to access the object.
- The port is configurable. Remember it for the client.
- We use here a tcp channel with a binary formatter.

Last, we need to change the Main method in the server to read the remoting configuration from the Xml file:
-----------------------------------------------

[STAThread]
static void Main()
{
   string fileName = "server.exe.config";
   RemotingConfiguration.Configure(fileName);
   Application.Run(new Form1());
}

----------------------------------------------
Look how easy it is to configure remoting this way (However, using configuration files has its disadvantages).

Step 5 - Create the Client.
Add an application configuration file to the client (same as server).
It should look like this:
----------------------------------------

<?xmlversion="1.0"encoding="utf-8"?>
<configuration>
   <system.runtime.remoting>
      <application name="MyClient.exe">
         <client>
            <wellknowntype="Common.IObjectFactory,
Common" url="tcp://localhost:16784/MyServer/
ObjectFactory.soap
"/>
         </client>
         <channels>
            <channel ref="tcp"port="0">
               <serverProviders>
                  <formatter ref="binary" typeFilterLevel="Full"/>
               </serverProviders>
               <clientProviders>
                  <formatter ref="binary"/>
               </clientProviders>
            </channel>
         </channels>
      </application>
   </system.runtime.remoting>
</configuration>

----------------------------------------
This is the client side configuration file. Pay attantion to the use of interface (IObjectFactory) instead of concrete class (the client does not have a reference to the concrete class), and the use of the server URI for the object.

Next we change the Main method in the client to look like this:
----------------------------------------

[STAThread]
static void Main()
{
   string fileName = "client.exe.config";
   RemotingConfiguration.Configure(fileName);
   Application.Run(new Form1());
}

----------------------------------------

Step 6 - Add a RemotingHelper class to Common project.
This will generically create the global instances in the server and will serve as a Factory for server classes. (This is taken from Ingo Rammar's excellent book "Advanced .NET Remoting")
----------------------------------------

using System;
using System.Collections;
using System.Runtime.Remoting;
namespace Common
{
   public class RemotingHelper
   {
      internal static bool _isInit = false;
      private static IDictionary _wellKnownTypes;

      public static object GetObject(Type type)
      {
         if (!_isInit)
            InitTypeCache();
         WellKnownClientTypeEntry entr = (WellKnownClientTypeEntry) _wellKnownTypes[type];
         if (entr==null)
            throw new RemotingException("Type not found");
         return Activator.GetObject(entr.ObjectType,entr.ObjectUrl);
      }
      private static void InitTypeCache()
      {
         _wellKnownTypes = new Hashtable();
         foreach(WellKnownClientTypeEntry entr in RemotingConfiguration.
GetRegisteredWellKnownClientTypes())
            _wellKnownTypes.Add(entr.ObjectType,entr);
         _isInit = true;
      }
   }
}

------------------------------------------------
This class is used by the client to retrieve classes that are marshaled by referenced (live n the server process). We use the GetObject method whenever we need such object. It is a very generic and powerful class.

That is it. We have all that we need.

Example of usage

Open Client Form1 (designer) and drag a button and two TextBoxes.
Double-click on the button, and add this code to it:
------------------------------------------------

private void button1_Click(object sender, EventArgs e)
{
   IObjectFactory fact = (IObjectFactory) RemotingHelper.GetObject(typeof(IObjectFactory));
   IShareableObject share = fact.GetObject();
   textBox1.Text = share.X.ToString();
   textBox2.Text = share.Message;
}

-------------------------------------------------
We get a proxy to the ObjectFactory object in the server using the RemotingHelper class. Then we retrieve a copy of the ShareableObject object by calling fact.GetObject method. Then we put the values of X and Message in the GUI. You will see 10 and 123 (look at Step 4 - ObjectFactory class).
If you put a breakpoint on the beginning of the event handler, you will see that fact is a Transparent Proxy (This is created automatically for us instead of creating a regular object). This is an indication that we are indeed using a remote object. The share object however is of type ShareableObject (You can see it as a regular class - it lives in the client process).

4 Comments:

At 9:49 PM, December 27, 2005, Anonymous Anonymous said...

how do i show the client form?

 
At 10:14 AM, December 28, 2005, Blogger j1hammer said...

It is supposed to show automatically.
If not then check that you client Main method contains the following line:
Application.Run( new Form1());

 
At 5:14 PM, August 30, 2006, Anonymous Anonymous said...

Hi,
Is it possible to do this:

1. Client is a windows application.
2. Server is hosted in IIS.
3. Client contacts server with some data and server checks using that data in a .mdb database on the server side.
4. Server returns some values to client?

Appreciate your help.
Thanks.
funpal4you@gmail.com

 
At 11:00 PM, August 30, 2006, Blogger j1hammer said...

Is it possible to do this:

1. Client is a windows application.
2. Server is hosted in IIS.
3. Client contacts server with some data and server checks using that data in a .mdb database on the server side.
4. Server returns some values to client?



I suggest you look into web services. They are exactly what you need. Web services are slightly different from Remoting, but they are very easy to use.

 

Post a Comment

<< Home

Feel free to use everything here. Add links to my site if you wish.

Do not copy anything to other sites without adding link to here.

All the contents of the site belong to Yariv Hammer.