Types of Invocation

The .NET Framework provides three possibilities to call methods on remote objects (no matter if they are Singleton, SingleCall, or published objects). You can execute their methods in a synchronous, asynchronous, or asynchronous oneway fashion.

Synchronous calls are basically what I showed you in the preceding examples. The server's remote method is called like a common method, and the client blocks (waits) until the server has completed its processing. If an exception occurs during execution of the remote invocation, the exception is thrown at the line of code in which you called the server.

Asynchronous calls are executed in a two-step process. (Asynchronous calls are discussed in more detail in Chapter 6.) The first step triggers the execution but does not wait for the method's response value. The program flow continues on the client. When you are ready to collect the function's response, you have to call another function that checks if the server has already finished processing your request; if not, it blocks until finalization. Any exception thrown during the call of your method will be rethrown at the line of code where you collect the response. Even if the server has been offline, you won't be notified beforehand.

The last kind of function is a little different from the preceding ones. With asynchronous one-way methods, you don't have the option of receiving return values or getting an exception if the server has been offline or otherwise unable to fulfill your request. The .NET Remoting Framework will just try to call the methods on the remote server and won't do anything else.

Synchronous Calls

As I've mentioned, synchronous calls are the usual way of calling a function in the .NET Framework. The server will be contacted directly and, except when using multiple client-side threads, the client code will block until the server has finished executing its method. If the server is unavailable or an exception occurs while carrying out your request, the exception will be rethrown at the line of code where you called the remote method.

Using Synchronous Calls

In the following series of examples for the different types of invocation, you use a common server and a shared assembly called General.dll (you'll see some slight modifications in the last part). This server just provides you with a Singleton object that stores an int as its state and has an additional method that returns a String. You'll use this later to demonstrate the collection of return values when using asynchronous calls.

Defining the General.dll

In Listing 3-11, you see the shared General.dll in which the necessary interface is defined.

Listing 3-11: The Shared Assembly's Source Code

start example
 using System; using System.Runtime.Remoting.Messaging; namespace General {    public abstract class BaseRemoteObject: MarshalByRefObject    {       public abstract void setValue(int newval);       public abstract int getValue();       public abstract String getName();    } } 
end example

Creating the Server

The server, shown in Listing 3-12, implements the defined methods with the addition of making the setValue() and getName() functions long-running code. In both methods, a five-second delay is introduced so you can see the effects of long-lasting execution in the different invocation contexts.

Listing 3-12: A Server with Some Long-Running Methods

start example
 using System; using System.Runtime.Remoting; using General; using System.Runtime.Remoting.Channels.Http; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Messaging; using System.Collections; using System.Threading; namespace Server {    class MyRemoteObject: BaseRemoteObject    {       int myvalue;       public MyRemoteObject()       {          Console.WriteLine("MyRemoteObject.Constructor: New Object created");       }       public override void setValue(int newval)       {          Console.WriteLine("MyRemoteObject.setValue(): old {0} new {1}",                                myvalue,newval);          // simulate a long running action          Console.WriteLine("      .setValue() -> waiting 5 sec before setting" +                              "value");          Thread.Sleep(5000);          myvalue = newval;          Console.WriteLine(" .setValue() -> value is now set");       }       public override int getValue()       {          Console.WriteLine("MyRemoteObject.getValue(): current {0}",myvalue);          return myvalue;       }       public override String getName()       {          Console.WriteLine("MyRemoteObject.getName(): called");          // simulate a long running action          Console.WriteLine("     .getName() -> waiting 5 sec before continuing");          Thread.Sleep(5000);          Console.WriteLine("     .getName() -> returning name");          return "John Doe";       }    }    class ServerStartup    {       static void Main(string[] args)       {          Console.WriteLine ("ServerStartup.Main(): Server started");          HttpChannel chnl = new HttpChannel(1234);          ChannelServices.RegisterChannel(chnl);          RemotingConfiguration.RegisterWellKnownServiceType(                typeof(MyRemoteObject),                "MyRemoteObject.soap",                WellKnownObjectMode.Singleton);          // the server will keep running until keypress.          Console.ReadLine();       }    } } 
end example

Creating the Client

The first client, which is shown in Listing 3-13, calls the server synchronously, as in all preceding examples. It calls all three methods and gives you statistics on how long the total execution took.

Listing 3-13: The First Client Calls the Methods Synchronously

start example
 using System; using System.Runtime.Remoting; using General; using System.Runtime.Remoting.Channels.Http; using System.Runtime.Remoting.Channels.Tcp; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Proxies; using System.Threading; namespace Client {    class Client    {       static void Main(string[] args)       {          DateTime start = System.DateTime.Now;          HttpChannel channel = new HttpChannel();          ChannelServices.RegisterChannel(channel);          BaseRemoteObject obj = (BaseRemoteObject) Activator.GetObject(             typeof(BaseRemoteObject),             "http://localhost:1234/MyRemoteObject.soap");          Console.WriteLine("Client.Main(): Reference to rem.obj. acquired");          Console.WriteLine("Client.Main(): Will set value to 42");          obj.setValue(42);          Console.WriteLine("Client.Main(): Will now read value");          int tmp = obj.getValue();          Console.WriteLine("Client.Main(): New server side value {0}", tmp);          Console.WriteLine("Client.Main(): Will call getName()");          String name = obj.getName();          Console.WriteLine("Client.Main(): received name {0}",name);          DateTime end = System.DateTime.Now;          TimeSpan duration = end.Subtract(start);          Console.WriteLine("Client.Main(): Execution took {0} seconds.",                          duration.Seconds);          Console.ReadLine();       }    } } 
end example

As the calls to the long-running methods getName() and setValue() are expected to take roughly five seconds each, and you have to add a little overhead for .NET Remoting (especially for the first call on a remote object), this example will take more than ten seconds to run.

You can see that this assumption is right by looking at the client's output in Figure 3-15. The total client execution takes 12 seconds. When looking at the server's output in Figure 3-16, note that all methods are called synchronously. Every method is finished before the next one is called by the client.

click to expand
Figure 3-15: Client's output when using synchronous calls

click to expand
Figure 3-16: Server's output when called synchronously

Asynchronous Calls

In the synchronous calls example, you saw that waiting for every method to complete incurs a performance penalty if the calls themselves are independent; the second call doesn't need the output from the first. You could now use a separate thread to call the second method, but even though threading is quite simple in .NET, it would probably render the application more complex if you use a distinct thread for any longer lasting remote function call. The .NET Framework provides a feature, called asynchronous delegates, that allows methods to be called in an asynchronous fashion with only three lines of additional code.

Delegate Basics

A delegate is, in its regular sense, just a kind of an object-oriented function pointer. You will initialize it and pass a function to be called when the delegate is invoked. In .NET Framework, a delegate is a subclass of System.MulticastDelegate, but C# provides an easier way to define a delegate instead of declaring a new Class.

Declaring a Delegate

The declaration of a delegate looks quite similar to the declaration of a method:

 delegate <ReturnType> <name> ([parameters]); 

As the delegate will call a method at some point in the future, you have to provide it with a declaration that matches the method's signature. When you want a delegate to call the following method:

 public String doSomething(int myValue) 

you have to define it as follows:

 delegate String doSomethingDelegate (int myValue); 
Note 

The delegate's parameter and return types have to match those of the method.

Remember that the delegate is in reality just another class, so you cannot define it within a method's body, only directly within a namespace or another class!

Asynchronously Invoking a Delegate

When you want to use a delegate, you first have to create an instance of it, passing the method to be called as a constructor parameter:

 doSomethingDelegate del = new doSomethingDelegate(doSomething); 

Note 

When passing the method to the constructor, be sure not to include an opening or closing parenthesis- ( or ) -as in doSomething(). The previous example uses a static method doSomething in the same class.When using static methods of other classes, you have to pass SomeClass.someMethod, and for instance methods, you pass SomeObject.doSomething.

The asynchronous invocation of a delegate is a two-step process. In the first step, you have to trigger the execution using BeginInvoke(), as follows:

 IAsyncResult ar = del.BeginInvoke(42,null,null); 
Note 

BeginInvoke() behaves a little strangely in the IDE. You won't see it using IntelliSense, as it is automatically generated during complication, The parameters are the same as the method parameters, according to the delegate definition, followed by two other objects; you won't be using these two objects in the following examples, instead passing null to BeginInvoke().

BeginInvoke() then returns an IAsyncResult object that will be used later to retrieve the method's return values. When ready to do so, you call EndInvoke() on the delegate passing the IAsyncResult as a parameter. The EndInvoke() method will block until the server has completed executing the underlying method.

 String res = del.EndInvoke(ar); 
Note 

EndInvoke() will not be visible in the IDE either. The method takes an IAsyncResult as a parameter, and its return type will be defined in the delegate's declaration.

Creating an Example Delegate

In Listing 3-14, a delegate is used to asynchronously call a local function and wait for its result. The method returns a String built from the passed int parameter.

Listing 3-14: Using a Delegate in a Local Application

start example
 using System; namespace SampleDelegate {    class SomethingClass    {       delegate String doSomethingDelegate(int myValue);       public static String doSomething(int myValue)       {          return "HEY:" + myValue.ToString();       }       static void Main(string[] args)       {         doSomethingDelegate del = new doSomethingDelegate(doSomething);         IAsyncResult ar = del.BeginInvoke(42,null,null);          // ... do something different here         String res = del.EndInvoke(ar);          Console.WriteLine("Got result: '{0}'",res);          //wait for return to close          Console.ReadLine();       }    } } 
end example

As expected, the application outputs "HEY:42" as you can see in Figure 3-17.

click to expand
Figure 3-17: The sample delegate

Implementing the New Client

In the new remoting client, shown in Listing 3-15, you see how to change the calls to getName() and setValue() to use delegates as well. Your client then invokes both delegates and subsequently waits for their completion before synchronously calling getValue() on the server. In this instance, you use the same server application as in the preceding example.

Listing 3-15: The New Client Now Using Asynchronous Delegates

start example
 using System; using System.Runtime.Remoting; using General; using System.Runtime.Remoting.Channels.Http; using System.Runtime.Remoting.Channels.Tcp; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Proxies; using System.Threading; namespace Client {    class Client    {      delegate void SetValueDelegate(int value);      delegate String GetNameDelegate();       static void Main(string[] args)       {          DateTime start = System.DateTime.Now;          HttpChannel channel = new HttpChannel();          ChannelServices.RegisterChannel(channel);          BaseRemoteObject obj = (BaseRemoteObject) Activator.GetObject(             typeof(BaseRemoteObject),             "http://localhost:1234/MyRemoteObject.soap");          Console.WriteLine("Client.Main(): Reference to rem.obj. acquired");          Console.WriteLine("Client.Main(): Will call setValue(42)");         SetValueDelegate svDelegate = new SetValueDelegate(obj.setValue);         IAsyncResult svAsyncres = svDelegate.BeginInvoke(42,null,null);          Console.WriteLine("Client.Main(): Invocation done");          Console.WriteLine("Client.Main(): Will call getName()");         GetNameDelegate gnDelegate = new GetNameDelegate(obj.getName);         IAsyncResult gnAsyncres = gnDelegate.BeginInvoke(null,null);          Console.WriteLine("Client.Main(): Invocation done");          Console.WriteLine("Client.Main(): EndInvoke for setValue()");         svDelegate.EndInvoke(svAsyncres);          Console.WriteLine("Client.Main(): EndInvoke for getName()");          String name = gnDelegate.EndInvoke(gnAsyncres);          Console.WriteLine("Client.Main(): received name {0}",name);          Console.WriteLine("Client.Main(): Will now read value");          int tmp = obj.getValue();          Console.WriteLine("Client.Main(): New server side value {0}", tmp);          DateTime end = System.DateTime.Now;          TimeSpan duration = end.Subtract(start);          Console.WriteLine("Client.Main(): Execution took {0} seconds.",                          duration.Seconds);          Console.ReadLine();       }    } } 
end example

When looking in the client's output in Figure 3-18, you can see that both long-running methods have been called at nearly the same time. This results in improved runtime performance, taking the execution time down from 12 seconds to 8 at the expense of making the application slightly more complex.

click to expand
Figure 3-18: Client output when using asynchronous calls

The server output in Figure 3-19 shows that both methods have been entered on the server at the same time without blocking the client.

click to expand
Figure 3-19: Server's output when called asynchronously

Asynchronous One-Way Calls

One-way calls are a little different from asynchronous calls in the respect that the .NET Framework does not guarantee their execution. In addition, the methods used in this kind of call cannot have return values or out parameters. You also use delegates to call one-way methods, but the EndInvoke() function will exit immediately without checking if the server has finished processing yet. No exceptions are thrown, even if the remote server is down or the method call is malformed. Reasons for using these kind of methods (which aren't guaranteed to be executed at all) can be found in uncritical logging or tracing facilities, where the nonexistence of the server should not slow down the application.

Demonstrating an Asynchronous One-Way Call

You define one-way methods using the [OneWay] attribute. This happens in the defining metadata (in the General.dll in these examples) and doesn't need a change in the server or the client.

Defining the General.dll

The attribute [OneWay()] has to be specified in the interface definition of each method that will be called this way. As shown in Listing 3-16, you change only the setValue() method to become a one-way method; the others are still defined as earlier.

Listing 3-16: The Shared Interfaces DLL Defines the One-Way Method

start example
 using System; using System.Runtime.Remoting.Messaging; namespace General {    public abstract class BaseRemoteObject: MarshalByRefObject    {       [OneWay()]       public abstract void setValue(int newval);       public abstract int getValue();       public abstract String getName();    } } 
end example

Implementing the Client

On the server side, no change is needed, so you can directly look at the client. In theory, no modification is needed for the client as well, but extend it a little here to catch the eventual exception during execution, as shown in Listing 3-17.

Listing 3-17: Try/Catch Blocks Are Added to the Client

start example
 using System; using System.Runtime.Remoting; using General; using System.Runtime.Remoting.Channels.Http; using System.Runtime.Remoting.Channels.Tcp; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Proxies; using System.Threading; namespace Client {    class Client    {       delegate void SetValueDelegate(int value);       static void Main(string[] args)       {          HttpChannel channel = new HttpChannel();          ChannelServices.RegisterChannel(channel);          BaseRemoteObject obj = (BaseRemoteObject) Activator.GetObject(             typeof(BaseRemoteObject),             "http://localhost:1234/MyRemoteObject.soap");          Console.WriteLine("Client.Main(): Reference to rem.obj. acquired");          Console.WriteLine("Client.Main(): Will call setValue(42)");         SetValueDelegate svDelegate = new SetValueDelegate(obj.setValue);         IAsyncResult svAsyncres = svDelegate.BeginInvoke(42,null,null);          Console.WriteLine("Client.Main(): Invocation done");          Console.WriteLine("Client.Main(): EndInvoke for setValue()");          try          {             svDelegate.EndInvoke(svAsyncres);             Console.WriteLine("Client.Main(): EndInvoke returned successfully");          }          catch (Exception e)          {             Console.WriteLine("Client.Main(): EXCEPTION during EndInvoke");          }          // wait for keypress          Console.ReadLine();       }    } } 
end example

When this client is started, you will see the output in Figure 3-20 no matter whether the server is running or not.

click to expand
Figure 3-20: Client output when using one-way methods

As shown in Listing 3-18, you can now change the method in General.dll back to a standard method (non-one-way) by commenting out the [OneWay()] attribute.

Listing 3-18: Removing the [OneWay()] Attribute

start example
 using System; using System.Runtime.Remoting.Messaging; namespace General {    public abstract class BaseRemoteObject: MarshalByRefObject    {       // no more one-way attribute [OneWay()] public abstract void setValue(int newval);       public abstract int getValue();       public abstract String getName();    } } 
end example

Recompilation and a restart of the client (still without a running server) yields the result in Figure 3-21: an exception is thrown and a corresponding error message is output.

click to expand
Figure 3-21: Client output when removing the [OneWay()] attribute

When you now start the server (and restart the client), you get the output shown in Figure 3-22, no matter if you used the [OneWay()] attribute or not. The interesting thing is that when using [OneWay()], the call to EndInvoke() finishes before the server completes the method. This is because in reality the client just ignores the server's response when using one-way method calls.

click to expand
Figure 3-22: Output on the server-independent of [OneWay()] attribute

Caution 

Always remember that the client ignores the server's output and doesn't even check if the server is running when using one-way methods!




Advanced  .NET Remoting C# Edition
Advanced .NET Remoting (C# Edition)
ISBN: 1590590252
EAN: 2147483647
Year: 2002
Pages: 91
Authors: Ingo Rammer

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net