Web solution, Websites help, Java help, C & C# Language help

Friday, December 14, 2007

C Language Help, C Language Tutorials, C Language Programming, C Language Tricks { The C# Language }




C Language Help, C Language Tutorials, C Language Programming, C Language Tricks
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");

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.



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.



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



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.



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

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

{

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"));

}

}

}



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");

}

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. 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

{

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

{

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 user-defined 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 could'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

{

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());

}

}

}

}



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()

{

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 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 System;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Tcp;

using 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

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.

No comments: