Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Absolute beginner's introduction to remoting

0.00/5 (No votes)
4 Apr 2002 4  
Introduces .NET remoting and the use of asynchronous callbacks using simple code snippets

Introduction

Remoting allows you to work with objects remotely. It is a lot more efficient than SOAP based web services but obviously it can only be used within intranets. On the internet you would have problems with firewalls, and an overall lack of security.

In really simple terms you have a client program running on a machine invoking functions on an object that is instantiated on a different machine. Let's jump into some really short and crisp code snippets. Usually I find that it's easier to understand a concept by looking at a small code snippet than if you read a 2000 word essay giving you all kinds of complicated theory. Of course any extra theory you learn is always going to help in a lot of ways.

The interface

The first thing we do is to create an interface called MyInterface. Then we'll compile it into a dll. Later we'll derive our remote class from this interface. Now, what this does for us is that on the client machine we can use this interface. Otherwise we'd have had to use the class directly from the client machine which pretty much makes the whole exercise a little futile.

// csc /t:library MyInterface.cs


public interface MyInterface
{
    int FunctionOne(string str);
}

The object

Now that we have our interface ready, let's create our class which we will invoke from a remote machine. We derive this class from MarshalByRefObject and MyInterface. Any class derived from MarshalByRefObject allows remote clients to invoke it's methods, which is just what we are trying to do here.

// csc /t:library /r:MyInterface.dll RemoteObject.cs


using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;

public class MyRemoteClass : MarshalByRefObject,MyInterface
{
    public int FunctionOne(string str)
    {		
        return str.Length;
    }
}

The server

Now, let's write our remote server.

// csc /r:RemoteObject.dll RemoteServer.cs

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

public class MyServer
{
    public static void Main()
    {
        TcpChannel m_TcpChan = new TcpChannel(9999);
        ChannelServices.RegisterChannel(m_TcpChan);
        RemotingConfiguration.RegisterWellKnownServiceType(
            Type.GetType("MyRemoteClass,RemoteObject"),
            "FirstRemote",WellKnownObjectMode.SingleCall);
        System.Console.WriteLine("Press ENTER to quit");
        System.Console.ReadLine();
    }
}

First we create a TcpChannel object which we use to transport messages across our remoting boundary. As you can see, I have also selected 9999 as the TCP port to listen on. Now you can understand how this would cause a hell of a lot of problems with firewalls. We use ChannelServices.RegisterChannel to register our TcpChannel with the channel services. Don't ask me what exactly goes inside that function. All I could figure out is that if you don't register the channel, remoting won't work. Now we use RemotingConfiguration.RegisterWellKnownServiceType to register our MyRemoteClass object as a well-known type. Now this object can be remoted.

The first time I saw the huge function name, I thought it was some kind of joke. But then the .NET framework has thousands of functions and classes and thus I guess they didn't really have a choice. It's a lot better than calling it __rwkst(...), I guess. I am using the word FirstRemote here which will be used as part of the URL that the client uses to access the remote object. I specified the SingleCall mode here, which means a new instance is created for each remote call. I could have used Singleton in which case one object would have been instantiated and used by all connecting clients. I guess if this was an advanced article [which it is not] written by an advanced level programmer [which I am not], there would have been some explanation as to the differences between the two modes.

The client

Okay, now run your server on one of the machines. Now let's take a look at the client which can be run on the same or on a separate machine. I had to run it on the same machine for some complicated technical reasons. Well to be honest, the complicated technical reason was that I had only one machine at home.

// csc /r:MyInterface.dll client.cs


using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

class MyClient
{
    public static void Main()
    {
        TcpChannel m_TcpChan = new TcpChannel();
        ChannelServices.RegisterChannel(m_TcpChan);
        MyInterface m_RemoteObject = (MyInterface)
            Activator.GetObject(typeof(MyInterface),
            "tcp://earth:9999/FirstRemote");
        Console.WriteLine(m_RemoteObject.FunctionOne("Nish"));
    }
}

Just as in the server, we have a TcpChannel object, though in this case we don't specify a port. We also use ChannelServices.RegisterChannel to register the channel. We use Activator.GetObject to get an instance of the MyInterface object. As you can see I have used the hostname 'earth' in my URL, as that's my machine's hostname. In your case you'd have to replace it with the name of the remote machine or it's IP address. The word 'FirstRemote' which forms part of the URL was what I had passed to RemotingConfiguration.RegisterWellKnownServiceType on the server. Now that I have got my object I can call its member functions. Well, that was not too difficult now, was it?

Asynchronous Callbacks

In our client we have used synchronous calls to the remote object. The problem with this, is that if the function takes quite a while to execute, our program hangs until the function returns. Of course you could start a worker thread each time you wanted to access a remote method, but isn't very efficient. And if by any chance you are passing shared data to the method and also getting some shared data back, you would have to take care of thread synchronization. The good news is that you don't have to do all that complicated stuff. You can use asynchronous callbacks.

// csc asyncclient.cs /r:MyInterface.dll


using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Messaging;

class MyClient
{
    public delegate int MyDelegate(string s);
    public static void Main()
    {
        TcpChannel m_TcpChan = new TcpChannel();
        ChannelServices.RegisterChannel(m_TcpChan);
        MyInterface m_RemoteObject = (MyInterface)
            Activator.GetObject(typeof(MyInterface),
            "tcp://earth:9999/FirstRemote");    
        MyDelegate m_delegate = 
            new MyDelegate(m_RemoteObject.FunctionOne);
        m_delegate.BeginInvoke("Amazing", 
            new AsyncCallback(MyCallBack),null);
        System.Console.WriteLine("Press ENTER to quit");
        System.Console.ReadLine();
    }

    public static void MyCallBack(IAsyncResult ar)
    {
        int l = ((MyDelegate)((AsyncResult)ar).AsyncDelegate).EndInvoke(ar);
        Console.WriteLine(l);
    }
}

Here we have two delegates. Delegates are type safe function pointers. The first delegate is one we create called MyDelegate. We point this to the function we need to access from our remote object. Now we call the function BeginInvoke. The first parameter is the parameter we pass to the actual function. The second parameter is another delegate of the AsyncCallback delegate type. What happens now is that the remote function is called but control returns immediately and our program won't block. Later, when the remote object completes the execution of the function, our callback is called with an IAsyncResult parameter which encapsulates the results of an asynchronous operation on an asynchronous delegate. We use the AsyncDelegate property to get our delegate and cast it to MyDelegate and call EndInvoke on it to get the results of the remotely executed function. You must keep in mind that the remote function has already terminated and its termination has nothing to do with EndInvoke. EndInvoke simply gives us back the results of the operation.

Conclusion

I really do hope that the beginners who read this article have found this article useful. There is a lot more to remoting and if I do learn more stuff, I'll post more articles on it here.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here