Saturday, April 23, 2011

.Net Remoting - Using delegate for the Remote Functions in Both Sync, Async way


1. Introduction


In this article, I explain how will you use delegate on the functions exposed by the remote object. Also, I will explain how do we call those remote object functions in a synchronous way and in an asynchronous way. I do not want to explain threading here. But below is a very short note about Sync and Async function calls:

  1. The client function that makes a call to the remote function will wait for the completion of the remote function. This is a Synchronous call to the function exposed by the remote object.
  2. The client function that makes a call to the remote function will not wait for the completion of the remote function. This is Asynchronous call the function.

2. About the example


The Server in this example exposes a function that prints a running count taking some time. You can think of this function as long running task on the server. The function is just to simulate the long running process situation.

The client has two buttons and one function is calling the remote function in a synchronous way and other does the same in the Asynchronous way. The client uses the delegate of the same type to make a call to the remote functions.

When you click the Start Sync button, the count will be running on the server and once it is finished, the count on the form starts. The server shows the running count in the console window and client shows it in the text box. So, here the client will wait for the server completed the counts.

When you click the Start Async button, the count will run in parallel between a server and the calling client. That means, after the call the client will not wait for the server to complete its task. 

Fig 1. Example Application



3. Codes for The Server


The code for the server is similar to previous examples. So you will not see much explanation here repeated again. If you need much explanation on the server, please have a look at the First remoting article.

1) In the server, after creating the project a class called Counter is added and it is derived from MarshalByRef object. In the counter.cs file required namespace is included. This Counter class acts as the Remote class.

//RemSrv 01: Include required assemblies
using System.Runtime.Remoting;

2) The class has a constructor and a method PerformCount. This method will be called from the client using delegates. We will see about that in detail when we are moving to the client side coding. The code for this class is given below:

//RemSrv 02: Initialize the remoting object
public Counter()
{
    Console.WriteLine("Remote Object Created. " + Environment.NewLine);
}

//RemSrv 03: Perform the counting operation. This will take sometime and is useful to explain
//           How async call to this method is useful from the client end.
public void PerformCount()
{
    int x;

    for (x = 1; x < 10000; x++)
        Console.WriteLine("Current Count : " + x.ToString());

    Console.WriteLine("Counting is finished");
    return;
}

3) In the application entry, we are hosting the remote object under the name Counter. For more detail look at the basic article (the first one)

//RemSrv 04 : Required Assemblies
using System.Runtime;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

namespace RemotingDelegate
{
    class Program
    {
        static void Main(string[] args)
        {
            //RemSrv 05 : Create a communication channel (Server) and register it
            TcpServerChannel SrvrChnl = new TcpServerChannel(13340);
            ChannelServices.RegisterChannel(SrvrChnl, false);

            //RemSrv 06 : Register the Remote Class so that the Object can go
            //and sit on the Remote pool
            RemotingConfiguration.RegisterWellKnownServiceType
   (typeof(RemotingDelegate.Counter),
                "Counter", WellKnownObjectMode.SingleCall);

            //RemSrv 07 : Halt the server so that Remote client can access the object
            Console.WriteLine("Server is Running...");
            Console.WriteLine("Press Any key to halt the Server");
            Console.ReadKey();
        }
    }
}


4. Codes for The Client


The client is the windows application and the form details and what each UI is explained in section 2 of this article.

1) The below namespaces are included in the form to access the Remoting as well as very basic thread function Thread.Sleep. Also, note that the project reference for the server also included. Once the application is built properly then you can split the exes into server and client machines for testing purposes.

//Client 01: Include the required namespace
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Threading;
using RemotingDelegate;

2) Once we are ready with the required namespaces, two delegates of the same are declared at the class level. Actually, one delegate is sufficient, I kept two just to differentiate the way I am going to use it.

//Client 03: Declare delegates for Sync Call and Async Call
public delegate void SyncCall();
public delegate void AsyncCall();

3) The LocalCounter function here does the same job of the function PerformCount on the server. So there are two long running tasks, one at the server side and the other one is at the client side.

//Client 06: Start the Local Counter. Assume that It is a long running task.
private void LocalCounter()
{
    long x;

    lblDisplay.Text = "Starting the Local Count...";

    for (x = 1; x < 10000; x++)
    {
        txtCount.Text = x.ToString();
        Application.DoEvents();
    }

   lblDisplay.Text = "Local Count is Done.";
}

4) The click event handler for the button Start Sync first creates the proxy for the remote object and stores that in the variable cntObj. Then a delegate object of type SyncCall is created and it is pointing the remote function PerformCount. The function PerformCount is passed to the delegate object by using the proxy cntObj. Once the delegate fnCounter is ready, a call to the remote function PerformCount is made using the delegate. And after that a call to the local task (LocalCounter) also made. Below is the code:

private void btnSync_Click(object sender, EventArgs e)
{
    //Client 02: Get the Proxy for remote object
    Counter cntObj = (Counter)Activator.GetObject(typeof(Counter),
        "tcp://localhost:13340/Counter");

    //Client 04: Call the remote method through the delegate. This call is Synchronous.
    SyncCall fnCounter = new SyncCall(cntObj.PerformCount);
    fnCounter();

    //Client 05: Call the Local Counter
    LocalCounter();
}

Note that after making a call to the remote object (by the statement fnCounter), the execution will pause till the remote function finishes its task. Once the task is completed on the server, the execution resumes here on the client and the function LocalCounter starts executing. You can observe this by running the sample, the count on the server is displayed in the console window, once the count is completed, you will see an increment in the counter on the textbox of the form.

5) For making the asynchronous call, the delegate is created is in the same way as we did in the previous step. Below is the code:

//Client 07: Create the proxy for remote Object 
//and set the delegate to the required
//remote function.
Counter cntObj = (Counter)Activator.GetObject(
    typeof(Counter), 
    "tcp://localhost:13340/Counter");
AsyncCall FnCounter = new AsyncCall(cntObj.PerformCount);

6) Once the delegate is created, instead of directly calling the function, we are using the BeginInvoke method on the delegate. The first parameter is actually a call back that a server will call once it completes the operation. That is not covered here and I am leaving it to you to explore yourself. I am passing null for both the parameter. The return value is stored in the IAsyncResult. This is to do a check on the Server operation to make a safe call on the EndInvoke.

//Client 08: Call the remote method through the delegate. This is an 
//Asynchronous call.
IAsyncResult AR = FnCounter.BeginInvoke(null , null);

7) After the above call, we are making a call to the local computer function. But, here the client after making a call to the PerfomCount using the BeginInvoke method on the delegate immediately moves to the next statement, which is a function-call for local counting.  So there is no waiting for the server to complete its task.

//Client 09: Call the Local Counter. The Local Counter also 
//    run in parallel now, and we no need to
//           wait for the remote call completion. 
//    The remote counting method, Once Done, calls our
//           call back method CallBackHandler.
LocalCounter();

8) Finally, after making both the function run simultaneously, we are waiting at the end of the routine to make a call to the EndInvoke, which is the pair of its corresponding BeginInvoke. The IsCompleted property of the return value of the BeginInvoke method is used to test whether server function tied to the delegate is finished or not. Once we know the server is done with the operation, we can make a call to EndInvoke by passing the value returned from the BeginInvoke function call.

//Client 10: Test the Remote counting is finished or Not before 
//    invoking the EndInvoke method on
//           the delegate
while (!AR.IsCompleted)
    Thread.Sleep(500);
FnCounter.EndInvoke(AR);

9) The entire event Handling routine for the Start Async button is shown below:

private void btnAsync_Click(object sender, EventArgs e)
{

    //Client 07: Create the proxy for remote Object and set the 
 //    delegate to the required remote function.
    Counter cntObj = (Counter)Activator.GetObject(
     typeof(Counter), 
     "tcp://localhost:13340/Counter");
    AsyncCall FnCounter = new AsyncCall(cntObj.PerformCount);

    //Client 08: Call the remote method through the delegate. This is an 
 //    Asynchronous call.
    IAsyncResult AR = FnCounter.BeginInvoke(null , null);

    //Client 09: Call the Local Counter. The Local Counter also run in parallel 
 //    now, and we no need to wait for the remote call completion. 
 //    The remote counting method, Once Done, calls our call back 
 //    method CallBackHandler.
    LocalCounter();

    //Client 10: Test the Remote counting is finsihed or Not before invoking 
 //    the EndInvoke method on the delegate
    while (!AR.IsCompleted)
        Thread.Sleep(500);
    FnCounter.EndInvoke(AR);
}


5. Screen Shot of Sync and Async Call


Sync Call:




Note: The Server finished the count and Client not Yet Started.

Async Call:




Note: When the server is at the courting 32 and client is at 4533. It shows both the function is running in parallel.

Source code : Download

The above app is created in VS2005. If you have advanced IDE, Say yes to the conversion UI displayed.

No comments:

Post a Comment

Leave your comment(s) here.

Like this site? Tell it to your Firend :)