Remoting In

  • November 2019
  • PDF

This document was uploaded by user and they confirmed that they have the permission to share it. If you are author or own the copyright of this book, please report to us by using this DMCA report form. Report DMCA


Overview

Download & View Remoting In as PDF for free.

More details

  • Words: 6,179
  • Pages: 18
Remoting We embark upon this chapter by initiating the concept of 'remoting' and attempting to unravel it by using a diminutive example. In 'remoting', there exists a 'client' program on one machine, which may be located anywhere in the world, and a 'server' program on another machine. The client program thereafter, calls a function in the server program. The server, after having executed the function, delivers the return value of the function, back to the client. To effect and bring the abovementioned scenario to fruition, we have employed three programs named o.cs, s.cs and c.cs. Furthermore, to avoid calling the compiler command for each one of them individually, we have placed the compile commands in a batch file named a.bat. o.cs using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; public class zzz : MarshalByRefObject { public zzz() { Console.WriteLine("Constructor"); } ~zzz() { Console.WriteLine("Destructor"); } public String abc() { Console.WriteLine("Vijay Mukhi2"); return "Sonal"; } } s.cs using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; public class sss { public static void Main() { TcpChannel c = new TcpChannel(8085); ChannelServices.RegisterChannel(c); RemotingConfiguration.RegisterWellKnownServiceType( Type.GetType("zzz,o"), "eee", WellKnownObjectMode.SingleCall); System.Console.WriteLine("Press <enter> to exit..."); System.Console.ReadLine(); } } c.cs using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; public class ccc { public static void Main() { TcpChannel c = new TcpChannel(); ChannelServices.RegisterChannel(c); zzz a = (zzz)Activator.GetObject(typeof(zzz), "tcp://localhost:8085/eee");

1

if (a == null) System.Console.WriteLine("Could not locate server"); else Console.WriteLine(a.abc()); } } a.bat csc.exe /t:library o.cs csc.exe s.cs csc.exe /r:o.dll c.cs Output in server dos box Press <enter> to exit... Constructor Constructor Vijay Mukhi2 Destructor Destructor Output in client dos box Sonal

We simulate the 'remoting' behaviour by bringing two dos boxes into play. We run the client program in one box and the server program in the other box. The file o.cs is created with a class zzz, which derives from the class MarshalByRefObject. This abstract class, belonging to the System namespace, cannot be used in isolation. Hence, it requires a class that derives from it. A class of this kind permits remote objects to call code from it. There exist a constructor and destructor, which furnish information regarding the time of creation and destruction of an object. A function called abc is created, which displays 'Vijay Mukhi2' by using the WriteLine function and then, returns the name of 'Sonal'. Other programs can call the function abc, merely by deriving a class from MarshalByRefObject. The file o.cs is compiled into a dll, using the /target:library option. Understanding the abovementioned explanation does not require the genius of a rocket scientist. We shall now go ahead and write a simple server program named s.cs. c is an object, which is an instance of class TcpChannel, in the namespace System.Runtime.Remoting.Channels.Tcp. The constructor is assigned a port number, on which, the other machine can establish communication with it. The protocol used here is known as TCP/IP, where TCP stands for Transmission Control Protocol and IP implies Internet Protocol. The port number facilitates the creation of a channel between two machines. The number that we have selected for the port is 8085. This is because all the samples provided by Microsoft use this number. It must obviously be a lucky mascot for that Company. You may select any other number, as long the same number is used for the client and the server. Hereinafter, whenever a client connects on port 8085, the TCP/IP stack or the Windows Internet Software keeps the server apprised about the connection. The server, in turn, keeps a listening vigil on port 8085. The namespace, System.Runtime.Remoting contains a class ChannelServices, which has a static function RegisterChannel. This function accepts an IChannel interface as a parameter and thereafter, registers the Channel object with the channel services. The main activities of this function are not known. It suffices to say that, without this function, the TCPChannel object that has been created, would not be effective. The class TCPChannel derives from a large number of interfaces, such as IChannelSender, IChannel, IChannelReceiver, IDictionary, ICollection and IEnumerable.

2

The most imperative function that the server has to call is, the one that registers the function abc in class zzz, contained in the file o.dll. The name of this static function is RegisterWellKnownServiceType, of the class RemotingConfiguration, belonging to the namespace System.Runtime.Remoting. This function takes three parameters, beginning with a type object that represents the name of the assembly that contains the object to be registered. Our object is located in a dll. It could even be located in an executable. The name 'o' of the dll file is passed as the second parameter to the GetType Object. The first parameter to this function is the name of the class that represents the object. In case the class is located in a namespace, the full name has to be furnished. The GetType object returns a Type object. The second parameter of the function RegisterWellKnownServiceType, represents a URI, or a Universal Resource Identifier. It could be any original word that helps in identification of functions in the class zzz. This is referred to as an 'endpoint', where the object shall be published. The client does not connect to the class zzz directly, but to an endpoint. In our case, the URI is eee. It points indirectly to the class zzz, which is the object that needs to be remoted. Any string may be used, provided the same string is used by the client too. Had we wished to connect using ASP.Net, we would have been compelled to use zzz.soap, as the endpoint. The last parameter specifies the mode, which may assume any of the two values, i.e. SingleCall or Singleton. The data type is an enum named WellKnownObjectMode, which only embodies the two abovementioned values. The mode specifies the 'lifetime' of the object, i.e. the frequency of its creation. If we specify the mode as SingleCall, a new instance of the object zzz will be created each time a message is received. If it is assigned the value of Singleton, an instance of zzz will be created only once, and all the clients shall interact with it. On compiling s.cs, an executable named s.exe will be generated. The client code, which calls code from the remote object is placed in the file c.cs. In this file, we commence with the 'using' statement, in order to introduce the namespaces for the classes. Thereafter, a TcpChannel is created, but unlike a server, it is devoid of a port number. The port number assigned to the client is of no significance, since it is not binding on the client to listen to any request. Therefore, a random value is assigned as the port number. RegisterChannel registers the TcpChannel c. Next, we use the static function GetObject from the Activator class in the system namespace. This function accepts two parameters: • The first parameter is of data type Type, which represents the class that we wish to instantiate. • The second parameter is the URI of the object, which is to be instantiated. The URI follows a specific sequence, which is as follows: • It commences with the protocol that is required for communication. In our case, it is tcp. • This is followed by the machine name on which the server resides. In our case, everything is located on a single machine. Hence, we use the machine name 'localhost'. The machine name is by suffixed with a colon. • •

This is succeeded by the port number. Finally, the endpoint created in the server is to be specified. In this case, it is eee.

The abovementioned sequence has to be maintained, and all the above items are mandatory. This function returns an object that is an instance of class zzz, which we store in object 'a'. If the object 'a' has a value of null, it generates an error and displays a suitable error message. Otherwise, the function abc of the object 'a' is called, and the return value is displayed using the WriteLine function. On compiling c.cs, an error is generated. So, we provide a reference to the dll named o.dll, since the class zzz is present in it. In one of the dos boxes, we first run the server 's'. On the surface of it, it appears as though only the string "Press <enter> to exit" has been displayed. But in the background, the server has been registered as a sink for all those functions that need to call the function abc, from its endpoint eee.

3

If we merely press the Enter key, the ReadLine function, whose raison d'être is to prevent our server from quitting out, actually exits. When we run the client in a separate dos box, the server dos box displays 'Constructor' twice. This establishes the fact that the server has located the class zzz in the assembly o.dll and instantiated it. The server instantiates the object that is to be remoted, to enable the framework to read the metadata present in the assembly. This is part of its registration process. Thereafter, it displays the string 'VijayMukhi 2' in the server dos box, since the client has called the function abc. The client dos box displays 'Sonal', which is the return value of the function. The client program quits out gracefully, whereas, the server cools its heels, waiting for other calls. The server destroys the current object, and the framework keeps a listening vigil for other clients, which may be eager to connect on previously registered channels. When the Enter key is pressed, it results in the termination of the server program. Thus, the destructor gets called. We are, however, not certain whether an object perishes or survives when a program quits out. In any case, as far as the C# language is concerned, this is not in our control. The Constructor gets called twice: • On the first occasion, the framework reads the metadata . • On the second occurrence, the client interacts with an instance of the object zzz. The Destructor also gets called twice, since the framework brazenly destroys the two zzz objects which had been created. The client has to initially locate the server, and thereafter, connect to it, so that the zzz object gets instantiated. The client requires a function to execute this task and to return an object, which is an instance of zzz. However, the actual object dwells on another computer, which could be ensconced at a geographically dispersed location. Thereafter, the function GetObject returns a simulation of the zzz object, which it instantiates. For all practical purposes, it is under the delusion that it is the real zzz object, though in reality, the object has been indirectly created on another machine. Thus, GetObject returns a 'proxy' for the remote object. The literal meaning of the word 'proxy' is 'a surrogate of the original'. This proxy then directs the call to the original object residing on an alternative machine. The return value of the GetObject function is indicative of whether the object was successfully created on the remote machine or not. A null value indicates an error. The function abc is called from the remote zzz object, after the object has been successfully instantiated. However, a.abc() actually interacts with the proxy object, which in turn, forwards the call to the remote machine. The remotely located program executes the function abc, and thereafter, dispatches the return value over to the client. Two other programs named 'proxy' and 'stub' are also brought into play, in order to accomplish the task of 'remoting'. Let us now analyze this process from a fresh perspective. At the outset, we need to create two sub-directories, r2 and r3. c:\csharp>md r2 c:\csharp>md r3

Next, we are required to copy the server file s.cs and o.cs to the sub-directory r2, and the client file c.cs to the sub-directory r3. C:\csharp>copy s.cs r2 C:\csharp>copy o.cs r2 C:\csharp>copy c.cs r3 C:\csharp\r2> csc s.cs C:\csharp\r2> csc /t:library o.cs

4

C:\csharp\r3> csc c.cs c.cs(11,7): error CS0246: The type or namespace name 'zzz' could not be found (are you missing a using directive or an assembly reference?) c.cs(12,11): error CS0103: The name 'a' does not exist in the class or namespace 'ccc' c.cs(15,19): error CS0246: The type or namespace name 'a' could not be found (are you missing a using directive or an assembly reference?)

On compiling the two files placed in the sub-directory r2, no errors are generated. However, on compiling the client program in the sub-directory r3, an error is generated. We hit a roadblock at this juncture, since the file c.cs requires the assembly file in which, the remoted object, o.dll resides. You may argue that the prerequisite of placing the dll in the client's sub-directory defeats the very purpose of 'remoting'. However, the presence of the file is obligatory only for the metadata of the class zzz. The motivation for doing this will be elucidated shortly. C:\csharp\r3> copy c:\csharp\r2\o.dll C:\csharp\r3> csc c.cs /r:o.dll

Therefore, we copy the assembly file o.dll from the sub-directory r2, into the sub-directory r3, and thereafter, recompile it. Now, we add a tangy dash of lime to the mundane and bland routine given above, to give it a slight twist. The string VijayMukhi2 is modified to VijayMukhi3. The library is recompiled to produce a dll file in the sub-directory r2. C:\csharp\r2>edit o.cs public String abc() { Console.WriteLine("Vijay Mukhi3"); return "Sonal"; } C:\csharp\r2>csc /t:library o.cs

As a consequence, the copies of the function abc, which reside in the two subdirectories r2 and r3, become dissimilar.

Output in server dos box Press <enter> to exit... Constructor Constructor Vijay Mukhi3 Destructor Destructor Output in client dos box Sonal

The server program and the client program are run in their respective directories. On doing so, it becomes evident that, even though the client had a copy of the dll in its own sub-directory, it still called the function abc from o.dll, which was resident in the sub-directory of the server. Therefore, the output displayed is 'Vijay Mukhi3', and not 'Vijay Mukhi2'. In real life, the code placed in the directories r2 and r3 would actually be dwelling within geographically dispersed machines. The metadata of the object should be provided to the client, for the sole purpose of compilation.

5

o.cs using System; namespace nnn { public class zzz : MarshalByRefObject { public zzz() { Console.WriteLine("Constructor"); } ~zzz() { Console.WriteLine("Destructor"); } public String abc() { Console.WriteLine("Vijay Mukhi2"); return "Sonal " ; } public String pqr(string s) { Console.WriteLine("Sonal Mukhi2"); return "VMCI " + s ; } } } s.cs using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; public class sss { public static void Main() { TcpChannel c = new TcpChannel(8085); ChannelServices.RegisterChannel(c); RemotingConfiguration.RegisterWellKnownServiceType(Type.GetType( "nnn.zzz,o"), "eee", WellKnownObjectMode.SingleCall); System.Console.WriteLine("Press <enter> to exit..."); System.Console.ReadLine(); } } c.cs using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using nnn; public class ccc { public static void Main() { TcpChannel c = new TcpChannel(); ChannelServices.RegisterChannel(c); zzz a = (zzz)Activator.GetObject(typeof(zzz), "tcp://localhost:8085/eee"); if (a == null) System.Console.WriteLine("Could not locate server"); else

6

Console.WriteLine(a.abc()); Console.WriteLine(a.pqr("hell")); } } Output Client Sonal VMCI hell Output in server dos box Press <enter> to exit... Constructor Constructor Vijay Mukhi2 Constructor Sonal Mukhi2 Destructor Destructor Destructor

We shall now initiate a few modifications in the three files, o.cs, s.cs and o.cs. o.cs : Microsoft insists that all classes must reside in a namespace. Therefore, class zzz has been made part of the nnn namespace. A function named pqr has been introduced, which accepts a string parameter and returns the same string. The return value is prefixed with the acronym 'VMCI', which stands for my institute, i.e. Vijay Mukhi's Computer Institute. s.cs : The server has the name of the class nnn.zzz located within the function RegisterWellKnownServiceType. It has no regard for the functions belonging to the class zzz, since its primary focus is on the endpoint that represents the class. c.cs : Internally within the client, the class has been renamed as nnn.zzz. Since we abhor the process of writing the same ungainly names repeatedly, we use the keyword 'using' with nnn. As a result of this, every occurrence of zzz is replaced by nnn.zzz. So, the typeof keyword encounters the class name nnn.zzz. The function pqr is called with a single parameter. It is imperative to have the metadata available at this stage, since it contains the following: • The name of the class • All the functions that the class carries • The signatures of the functions. This is a form of code validation, which ensures that inappropriate signatures are not used while the functions in the client are being called. The output generated by these two programs is very predictable. There is just a slight departure from the expected output, in the case of the server window. Whenever a function from the remote server is to be executed, the constructor in the class is called. However, the output of the destructors is highly unpredictable. Therefore, it is possible for us to pass parameters to functions, even if the function is being remoted. Its demeanor is very similar to that of calling the function off the same machine. Interfaces sh.cs using System; namespace nnn { public interface iii {

7

String pqr(String s); } } o.cs using System; namespace nnn { public class zzz : MarshalByRefObject, iii { public zzz() { Console.WriteLine("Constructor"); } public String pqr(string s) { Console.WriteLine("Sonal Mukhi2"); return "VMCI " + s ; } } } s.cs using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; public class sss { public static void Main() { TcpChannel c = new TcpChannel(8085); ChannelServices.RegisterChannel(c); RemotingConfiguration.RegisterWellKnownServiceType(Type.GetType( "nnn.zzz,o"), "eee", WellKnownObjectMode.SingleCall); System.Console.WriteLine("Press <enter> to exit..."); System.Console.ReadLine(); } } c.cs using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using nnn; public class ccc { public static void Main() { TcpChannel c = new TcpChannel(); ChannelServices.RegisterChannel(c); iii a=(iii)Activator.GetObject(typeof(nnn.iii),"tcp://localhost:8085/eee"); if (a == null) System.Console.WriteLine("Could not locate server"); else { Console.WriteLine(a.pqr("hell")); } } }

8

a.bat del *.exe del *.dll csc.exe /t:library sh.cs csc.exe /t:library /r:sh.dll o.cs csc.exe s.cs csc.exe /r:sh.dll c.cs Output Server Press <enter> to exit... Constructor Constructor Sonal Mukhi2 Output Client VMCI hell

Interfaces are employed to overcome the requirement of placing the entire code of a class in an assembly, which is resident on the client machine. In the file sh.cs, a simple interface called iii is created in the namespace nnn, possessing a single function pqr. It is imperative for all classes that derive from this interface, to incorporate this function. The interface iii is compiled to a dll, and it does not require the /r: switch, since it does not refer to any external modules. The file carries only the metadata, and does not contain any code. In the object o.cs, class zzz is derived from both, the class MarshalByRefObject and the interface iii. Thus, it is essential to implement the function pqr in the class zzz. While compiling the file o.cs, a reference must be provided to the file sh.dll, which contains the metadata for the interface iii. The server does not get affected in the least by any modifications carried out in the object. This is by virtue of the framework, which registers a class zzz and not the interface iii. There is no necessity for the server to refer to any of the assemblies of o.dll and sh.dll. In the client program, the GetObject function instantiates an object of type iii, and not from the class zzz. Whether the code is called from an interface or a class, is of no significance. The prime benefit of using an interface is that the C# compiler does not need to introduce the assembly o.dll, which contains a sizeable amount of code. The assembly file sh.dll is adequate, since it provides the metadata of the interface. For example, Jack can create the interface iii on a separate machine and then, send across the file sh.dll to his client Jil. She can, in turn, use it on her machine, thereby meeting the requirements of the complier. The endpoint represents a zzz class, which in turn, represents an interface named iii, as well as, an object called MarshalByRefObject. A request for an iii object in the client does not generate any error, since the endpoint represents a zzz instance, which encompasses an iii instance. We use the file a.bat to compile all the above four files with a single stroke. This program evinces how we can separate the definition from the implementation. For the next program, the file s.cs remains unchanged. o.cs using System; namespace nnn { public class zzz : MarshalByRefObject { public zzz() { Console.WriteLine("Constructor");

9

} public String pqr(string s) { Console.WriteLine("Sonal Mukhi2"); return "VMCI " + s ; } } } c.cs using System; using System.Threading; using System.Runtime.Remoting; using System.Runtime.Remoting.Messaging; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; namespace nnn { public class ccc { public static ManualResetEvent e; public delegate String ddd(String s); public static void Main(string [] args) { e = new ManualResetEvent(false); TcpChannel c = new TcpChannel(); ChannelServices.RegisterChannel(c); zzz o = (zzz)Activator.GetObject(typeof(zzz), "tcp://localhost:8085/eee"); if (o == null) System.Console.WriteLine("Could not locate server"); else { AsyncCallback cb = new AsyncCallback(ccc.abc); ddd d = new ddd(o.pqr); IAsyncResult ar = d.BeginInvoke("Vijay", cb, null); Console.WriteLine(ar.IsCompleted); } e.WaitOne(); } public static void abc(IAsyncResult ar) { ddd d = (ddd)((AsyncResult)ar).AsyncDelegate; Console.WriteLine(d.EndInvoke(ar)); Console.WriteLine(ar.IsCompleted); e.Set(); } } } Output Client False VMCI Vijay True Output Server Press <enter> to exit... Constructor Constructor Sonal Mukhi2

In one of the earlier examples, we had called the pqr function from our client. At this point, a network message was transmitted to the server, which executed the function abc and dispatched the outcome back to the client.

10

There is a likelihood that the function executed in the server, could have taken hours to complete execution. So, the client would have had to wait eternally for the server. Therefore, this method is prodigiously wasteful in terms of time and resources. Thus, whenever a 'synchronous call' is made to the remote object, the client has to cease all activities, till it receives a response from the server. It would prove to be a lot more efficacious if the client could be allowed to continue doing its job, while the server is busy executing the function. On completion of the task, the server could notify the client. In other words, we do not want the client to 'wait' or 'block'. This mechanism can be implemented by employing an 'asynchronous call' to the function. In such cases, the call is made by the client. Therefore, only the code in the client needs modification. In the chapter on Threads, we had learnt about the ManualResetEvent event object. We had set it to a value of False. In such situations, the client will wait on the WaitOne function, till the Set function is called. A delegate is a type safe way of calling a method in an oblique manner. We have created a delegate ddd that accepts a string as a parameter, and it returns a string. The framework can execute a function in an asynchronous mode provided, we represent it as a delegate. Here, the callback function has been represented by the use of a delegate. For those who have earned their spurs on C/C++, it would be a revelation that delegates are actually 'pointers to functions'. The function pqr, which has been called in an asynchronous mode, accepts as well as returns a string. Therefore, it is amply evident that the delegate uses the same parameters and return types. When we were writing a book on Intermediate Language, IL (an assembler language to which all code in the .NET world gets converted), we had discovered that a delegate finally gets converted into a class. In our case, the delegate ddd contains a large number of functions, which get added to it. Two such functions are, BeginInvoke and EndInvoke. The main role of these functions is to call code that is written by the system or runtime. Hence, they are called native functions. Thereafter, an object cb, which is an instance of a delegate AsyncCallback, is created. The constructor of this class is passed a function abc, which is notified as soon as the remote function completes execution. The callback function abc is passed an IAsyncResult parameter, since this function matches the declaration of the delegate. Next, we create a delegate d of type ddd, whose constructor is assigned the function name pqr, which is to be executed in an asynchronous manner. The BeginInvoke function is called next. It is passed the parameter string, which is to be transmitted to the remote function named pqr. The second parameter to this function is another function called cb, which also requires to be notified after all the action has been wrapped up. This delegate allusively represents the function abc in the class ccc. The BeginInvoke function returns an IAsyncResult, which lies inert at this stage. The parameters passed to it are similar to the ones provided to our event handling function abc. The remote function may take excessive time to execute. Therefore, to avoid wasting time, the client continues with its work. Once the call is completed, the framework ensures that the function abc is called with the parameter representing the return value. To figure out the return value, we simply cast the parameter to a ddd object, and call the function EndInvoke off it. This return value is displayed in the client dos box. For Asynchronous calls, we create two delegates, one for the callback function, and the other for the remote method. Function BeginInvoke calls the remote function, which in turn, calls the function abc. When the call ceases, EndInvoke is called to receive the return values. The IAsyncResult consists of five properties, one of which is IsCompleted. Initially, this property returns False, since the asynchronous call has not been completed. However, in the function abc, it returns True, since the remote call has run its course and is done with. The EndInvoke function plays no role in ending the remote call. If we display the value contained in the IsCompleted property, prior to the EndInvoke function, a value of True would get displayed. sh.cs using System; namespace nnn

11

{ public class fff : MarshalByRefObject { public void xyz(String t) { Console.WriteLine(t); } } } >csc /t:library sh.cs o.cs using System; using System.Runtime.Remoting; namespace nnn { public class zzz : MarshalByRefObject { public zzz() { Console.WriteLine("Constructor"); } public String pqr(String s,fff f) { f.xyz("Hi"); Console.WriteLine("pqr called"); return "VMCI " + s; } } } >csc /t:library o.cs /r:sh.dll s.cs using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; public class sss { public static void Main() { TcpChannel c = new TcpChannel(8085); ChannelServices.RegisterChannel(c); RemotingConfiguration.RegisterWellKnownServiceType(Type.GetType( "nnn.zzz,o"), "eee", WellKnownObjectMode.SingleCall); System.Console.WriteLine("Press <enter> to exit..."); System.Console.ReadLine(); } } >csc s.cs c.cs using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; namespace nnn { public class ccc

12

{ public static void Main(string [] args) { TcpChannel c = new TcpChannel(8086); ChannelServices.RegisterChannel(c); fff f = new fff(); zzz o = (zzz)Activator.GetObject(typeof(nnn.zzz),"tcp://localhost:8085/eee"); if (o == null) System.Console.WriteLine("Could not locate server"); else Console.WriteLine(o.pqr("Vijay",f)); } } } >csc /r:sh.dll /r:o.dll c.cs Output in server dos box Constructor Constructor Pqr called Output in client dos box Hi VMCI Vijay

More often than not, the code for the server remains unchanged. In this program, our objective is to call a function remotely, having a parameter which is of a 'user-defined' type. Therefore, in the file sh.cs, we have a class called fff, which is derived from the class MarshalByRefObject. It contains a simple function called xyz, which displays a string that is passed as a parameter. The program centers around passing an instance of a userdefined class fff, as a parameter to the function pqr. In the object file o.dll, the class zzz has a function pqr, which accepts two parameters viz. a string s, and an object f. The object f is an instance of the class fff. Using the object f, the xyz function is called. This function is passed the string 'hi'. The rest of the code remains unaltered. The server, as mentioned earlier, is not modified. The client c.cs merely creates an object f, as an instance of class fff. This object is then passed as a parameter to pqr, without paying any cognizance to the fact that, the data shall be sent to another computer located in some other part of the world. The client couldn't care less, since this class is derived from the class MarshalByRefObject. This is the only prerequisite imposed on it, when instances of user-defined type are used as parameters. While compiling, references are to be given to the files o.dll and sh.dll, since both contain the metadata information required for error checking. The output in the client box displays 'Hi'. You should note that a port number has been furnished in the client program. Earlier, the port number had been provided only in the server program. By having two ports registered, the framework can use the two port numbers for different purposes. While one port number may be used to enable the Client to pass parameters to the server, the other may be used to permit the server to pass parameters to the client. This facilitates bi-directional communication between servers and clients, thereby, permitting the use of remote parameters.

Passing by value sh.cs using System; namespace nnn { [Serializable] public class fff

13

{ int j = 1; public fff() { Console.WriteLine("Constructor " + j); } public void xyz() { j++; } public int aaa() { return j; } } } o.cs using System; using System.Runtime.Remoting; namespace nnn { public class zzz : MarshalByRefObject { public zzz() { Console.WriteLine("Constructor"); } public fff pqr(fff o) { o.xyz(); o.xyz(); return o; } } } c.cs using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; namespace nnn { public class ccc { public static void Main(string [] args) { TcpChannel c = new TcpChannel(8086); ChannelServices.RegisterChannel(c); fff p = new fff(); zzz o = (zzz)Activator.GetObject(typeof(nnn.zzz),"Tcp://localhost:8085/eee"); if (o == null) System.Console.WriteLine("Could not locate server"); else { Console.WriteLine("Before " + p.aaa()); fff a = o.pqr(p); Console.WriteLine("After " + a.aaa() + " " + p.aaa()); } } }

14

} Server Output Constructor Constructor Client Output Constructor 1 Before 1 After 3 1

In the file sh.cs, we have two functions, aaa and xyz, and a single variable j. Their roles are as follows: • The constructor displays the value of variable j. • The function xyz increments its value by one. • The function aaa returns the value of the variable. The variable j could have been accessed directly, by making it public. Instead, the program uses functions to access it. The fff class, for some reason, has to be made Serializable. In the file o.cs, the object zzz contains a function pqr. This function accepts an fff instance as a parameter, and then returns a object of the same type. The xyz function in fff is called twice. An object that is an instance of a particular class, is different from another object, which may be an instance of the same class. There is no difference in the functions contained in the objects, since the code is identical amongst the instances. The variation occurs by virtue of the values of the instance variables, contained in these objects. Thus, the variable j will possess dissimilar values in the varied instances of the same class. In the client, a new fff instance named 'p' is created. The constructor is called, which results in display of the text "Constructor 1", since the current value of the object j is 1. The value in j is again printed, by calling the function aaa. Thereafter, function pqr is called with the parameter p, which is an fff object. The value of the variable j, which is contained in p, is presently 1. The function pqr calls the function xyz from the class fff twice. As a consequence of this, the value of j increases by 2. The fff instance, which is returned back by this function, is stored in 'a'. The WriteLine function then prints the value of j from both the fff objects, p and a. The object p is local to the client. Therefore, p.aaa would always display the value 1. The object 'a' that has been 'remoted', prints the value of j as 3. The names of objects are of no consequence at all, across computers. They are not visible in the IL code generated either. While remoting, the values in the local objects get handed over to a remote function. The modified value of the variable is displayed only in the new instance of the object. The local instance continues to display the original value. Singleton o.cs using System; namespace nnn { public class zzz : MarshalByRefObject { public zzz() { Console.WriteLine("Constructor"); } ~zzz() { Console.WriteLine("Destructor"); } public String pqr()

15

{ Console.WriteLine("pqr"); return "VMCI "; } } } s.cs using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; public class sss { public static void Main() { TcpChannel c = new TcpChannel(8085); ChannelServices.RegisterChannel(c); RemotingConfiguration.RegisterWellKnownServiceType(Type.GetType(" nnn.zzz,o"), "eee", WellKnownObjectMode.Singleton); System.Console.WriteLine("Press <enter> to exit..."); System.Console.ReadLine(); } } c.cs using System; using System.Threading; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; namespace nnn { public class ccc { public static void Main(string [] args) { TcpChannel c1 = new TcpChannel(); ChannelServices.RegisterChannel(c1); zzz o = (zzz)Activator.GetObject(typeof(zzz),"tcp://localhost:8085/eee"); Console.WriteLine(o.pqr()); Console.WriteLine(o.pqr()); } } } Server Output Press <enter> to exit... Constructor pqr pqr Destructor Client Output VMCI VMCI

The object zzz, which is to be remoted, resides in file o.cs and has a single function pqr, which returns 'VMCI'. It also contains a constructor and a destructor. However, its contents are nothing earth shaking. We have only carried out a single modification in the server program. The last parameter to the function RegisterWellKnownServiceType is changed to Singleton. This is suggestive of the fact that, all requests to the

16

remoting object are to be handled by the same instance of zzz. It does not create a separate instance of the object, every time the framework receives a request. As the name itself suggests, Singleton means 'single' or 'only one entity'. The client calls the function pqr twice. However, the constructor is called only once at the beginning, in order to read the metadata. The object is not created repeatedly on each request. Therefore, neither the constructor, nor the destructor is called. s.cs using using using using using

System; System.Runtime.Remoting; System.Runtime.Remoting.Channels; System.Runtime.Remoting.Channels.Tcp; System.Runtime.Remoting.Channels.Http;

namespace nnn { public class sss { public static void Main() { TcpChannel c = new TcpChannel(8085); ChannelServices.RegisterChannel(c); HttpChannel c1 = new HttpChannel(8086); ChannelServices.RegisterChannel(c1); RemotingConfiguration.RegisterWellKnownServiceType(Type.GetType(" nnn.zzz,o"),"eee", WellKnownObjectMode.Singleton); System.Console.WriteLine("Press <enter> to exit..."); System.Console.ReadLine(); } } } c.cs using System; using System.Threading; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; using System.Runtime.Remoting.Channels.Tcp; namespace nnn { public class ccc { public static void Main(string [] args) { HttpChannel c = new HttpChannel(); ChannelServices.RegisterChannel(c); TcpChannel c1 = new TcpChannel(); ChannelServices.RegisterChannel(c1); zzz o = (zzz)Activator.GetObject(typeof(zzz),"http://localhost:8086/eee"); zzz o1 = (zzz)Activator.GetObject(typeof(zzz),"tcp://localhost:8085/eee"); Console.WriteLine(o1.pqr()); Console.WriteLine(o1.pqr()); Console.WriteLine(o.pqr()); Console.WriteLine(o.pqr()); } } } Server Output Press <enter> to exit... Constructor pqr

17

pqr pqr pqr Destructor Client Output VMCI VMCI VMCI VMCI

The server has created two channels; one is the normal channel named TcpChannel, while the other one is called an HttpChannel. A separate port number has to be allotted to each of these channels. Therefore, the number 8085 is assigned to the TCP channel, while the number 8086 is assigned to the HTTP channel. The registration process does not delve into mundane issues, such as, the type of channel that has been used. The client creates both, an HttpChannel object, as well as a TcpChannel object. However, it does not specify the port numbers to the constructors. The GetObject function is used to specify a proxy. A major modification that has been incorporated here is, the HttpChannel being prefixed with http:, and the TcpChannel being prefixed with tcp:. The endpoint URI is retained as eee. The http channel connects to the server on port 8086, while the tcp channel connects to the server on port 8085. If you interchange the port numbers, the program will pause endlessly. The pqr function is called, by utilizing the http channel and the tcp channel. Since the call is made 4 times, 'pqr' is also displayed 4 times on our screen. We have not used a stopwatch to time the output generation; however, we were able to conclude that, the HTTP (Hyper Text Transfer Protocol), is much slower than the TCP (Transmission Control Protocol). This difference in the speeds of HTTP and TCP occurs because HTTP uses both SOAP (the Simple Object Access Protocol) and XML, which slows it down, whereas, TCP uses a binary protocol, which makes it relatively faster. A Singleton object preserves its state between function invocations, since it is not created each time that the function is invoked. A port number is not specified while registering a client channel, since the framework assigns it internally. This is dependent upon the task to be performed, such as, listening or connecting. The framework connects to a remote channel, using the URI specified. If we actually specify a port number in the client, it starts behaving like a server, i.e. it too listens on the specified port.

18

Related Documents

Remoting In
November 2019 15
Remoting
November 2019 13
Remoting
November 2019 20
Remoting
November 2019 16
Remoting In Dotnet
July 2020 4
Net Remoting
August 2019 40