Click here to Skip to main content
15,861,168 members
Articles / Programming Languages / C#
Article

Absolute beginner's introduction to remoting

Rate me:
Please Sign up or sign in to vote.
4.82/5 (85 votes)
4 Apr 2002CPOL5 min read 343.8K   167   84
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.

C#
// 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.

C#
// 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.

C#
// 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.

C#
// 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.

C#
// 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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
Nish Nishant is a technology enthusiast from Columbus, Ohio. He has over 20 years of software industry experience in various roles including Chief Technology Officer, Senior Solution Architect, Lead Software Architect, Principal Software Engineer, and Engineering/Architecture Team Leader. Nish is a 14-time recipient of the Microsoft Visual C++ MVP Award.

Nish authored C++/CLI in Action for Manning Publications in 2005, and co-authored Extending MFC Applications with the .NET Framework for Addison Wesley in 2003. In addition, he has over 140 published technology articles on CodeProject.com and another 250+ blog articles on his WordPress blog. Nish is experienced in technology leadership, solution architecture, software architecture, cloud development (AWS and Azure), REST services, software engineering best practices, CI/CD, mentoring, and directing all stages of software development.

Nish's Technology Blog : voidnish.wordpress.com

Comments and Discussions

 
GeneralMy vote of 4 Pin
paulsasik12-Oct-10 9:16
professionalpaulsasik12-Oct-10 9:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.