Quantcast
Channel: C# Help » Remoting
Viewing all articles
Browse latest Browse all 10

.Net Remoting – Part3 Remoting Examples

$
0
0
Preliminaries
Althought I have not done so, you can copy shared assemblies to the Global Assembly Cache (GAC) which is located in %WINDOWS%\Assembly. Alternatively you can refer to the shared assembly directly in your projects.
Asynchronous Call Example
Sometimes it is too costly to wait on a remote call to return. One solution may be to make the call in a seperate thread. This incurs its own cost on the client in terms of complexity and overhead. Another approach is to use the inherent asynchronous nature of delegates. Recall that a delegate is class that can be used to call a (set of) methods. The basic outline is: delegate return type delegate_name ([parameters]); Delagates have an asynchronous nature, when BeginInvoke is used on a delegate the CLR queues the call and returns to the caller immediately. The target method is subsequently called on a thread-pool thread. If a callback is specified in the BeginInvoke call, it is called when the target method returns. EndInvoke can be used to retreive any return value and in/out parameters. Here is our example.
// The Interface
using System;
namespace BaseLib
{
public abstract class BaseRemoteObject : MarshalByRefObject
{
public abstract void setValue(int pValue);
public abstract int getValue();
public abstract string getText();
}
}
// The Server
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Remoting.Messaging;
using System.Threading;

using BaseLib;

namespace Server
{

class MyRemoteObject : BaseRemoteObject
{
int mValue;
public MyRemoteObject()
{
Console.WriteLine("MyRemoteObject ctor called. New remote object created");
}

public override void setValue(int pValue)
{
Console.WriteLine("MyRemoteObject.setValue() old={0} new={1}", mValue, pValue);
Console.WriteLine("Waiting 5 sec …");
Thread.Sleep(5000);
mValue = pValue;
Console.WriteLine("Value is set");
}

public override int getValue()
{
Console.WriteLine("MyRemoteObject.getValue()={0}", mValue);
return mValue;
}

public override string getText()
{
Console.WriteLine("MyRemoteObject.getText() called");
Console.WriteLine("Waiting 5 sec …");
Thread.Sleep(5000);
Console.WriteLine("Returning text");
return "Narendra";
}
} // class MyRemoteObject

class ServerStartup
{
[STAThread]
static void Main(string[] args)
{
Console.WriteLine("Starting server on port 1237…");
HttpChannel chnl = new HttpChannel(1237);
ChannelServices.RegisterChannel(chnl);

RemotingConfiguration.RegisterWellKnownServiceType(
typeof(MyRemoteObject), "MyRemoteObject.soap", WellKnownObjectMode.Singleton);
Console.WriteLine("Press <enter> to exit");
Console.ReadLine();
}
}
}
</enter>

// The Client
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;

using BaseLib;

namespace ClientAsync
{
class Client
{
delegate void SetValueDelegate(int pValue);
delegate int GetValueDelegate();
delegate string GetTextDelegate();
[STAThread]
static void Main(string[] args)
{
DateTime start = System.DateTime.Now;
HttpChannel chnl = new HttpChannel();
ChannelServices.RegisterChannel(chnl);
BaseRemoteObject obj = (BaseRemoteObject) Activator.GetObject(typeof(BaseRemoteObject),
"http://localhost:1237/MyRemoteObject.soap");

SetValueDelegate svd = new SetValueDelegate(obj.setValue);
IAsyncResult arValSet = svd.BeginInvoke(625, null, null);
svd.EndInvoke(arValSet);

GetValueDelegate gvd = new GetValueDelegate(obj.getValue);
IAsyncResult arValGet = gvd.BeginInvoke(null, null);

Console.WriteLine("Client begin Async calls");
GetTextDelegate gtd = new GetTextDelegate(obj.getText);
IAsyncResult arTxt = gtd.BeginInvoke(null, null);

int iVal = gvd.EndInvoke(arValGet);
string str = gtd.EndInvoke(arTxt);
Console.WriteLine("Client end Async calls");

Console.WriteLine("getValue={0} getText={1}", iVal, str);

DateTime end = System.DateTime.Now;
TimeSpan ts = end.Subtract(start);
Console.WriteLine("Client: Remote Execution took {0} seconds", ts.Seconds);
}
}
}

Below is a figure of the asynchronous call. If you run the example you will see that on the server both remote methods are called without waiting implying that the client made both calls, the second call not waiting on the first to return.
MultiServer Example
A more general class of problems are handled by implementing more than one server. For example one server can be used to extract data from a database, while another server is used to format the data before passing it back to the client. This is also a very powerful technique for server load balancing. An Example:
// The Interface
using System;

namespace MultiServerLib
{
// Implemented by the primary server
public abstract class BaseRemotePrimaryObject : MarshalByRefObject
{
public abstract void setValue(int pValue);
public abstract int getValue();
}

// Implemented by the secondary server
public abstract class BaseRemoteSecondaryObject : MarshalByRefObject
{
public abstract void doWork(BaseRemotePrimaryObject brObj);
}
}

The common interface library consists of two abstract objects, one to be implemented by the primary server and the other by the secondary server. The secondary server takes an object that is created by the primary server as a parameter.
// The Primary Server
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

using MultiServerLib;

namespace PrimaryServer
{
class PrimaryServer : BaseRemotePrimaryObject
{
int mValue;

public override void setValue(int pValue)
{
Console.WriteLine("PrimaryServer.setValue old={0} new={1}", mValue, pValue);
mValue = pValue;

}

public override int getValue()
{
Console.WriteLine("PrimaryServer.getValue val={0}", mValue);
return mValue;
}

public PrimaryServer()
{
Console.WriteLine("PrimaryServer.ctor – remote singleton created");
}
}

class PrimaryServerStartup
{
[STAThread]
static void Main(string[] args)
{
TcpChannel chnl = new TcpChannel(1240);
ChannelServices.RegisterChannel(chnl);
RemotingConfiguration.RegisterWellKnownServiceType(typeof(PrimaryServer),
"PrimaryServer.tcp", WellKnownObjectMode.Singleton);
Console.WriteLine("Primary Remote Server Starting Port:1240 …");
Console.WriteLine("Press <enter> to exit");
Console.ReadLine();
}
}
}
</enter>

The primary server registers a SAO singleton.
// The Secondary Server
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

using MultiServerLib;

namespace SecondaryServer
{
class SecondaryServer : BaseRemoteSecondaryObject
{
public SecondaryServer()
{
Console.WriteLine("SecondaryServer.ctor – object created");
}

public override void doWork(BaseRemotePrimaryObject pObj)
{
Console.WriteLine("SecondaryServer.doWork()");
int i = pObj.getValue();
Console.WriteLine("SecondaryServer.doWork() getValue, got PrimaryServer value:{0}", i);
pObj.setValue(117);
Console.WriteLine("SecondaryServer.doWork() steValue, set PrimaryServer value:{0}",
pObj.getValue());
}
}

class ServerStartup
{
[STAThread]
static void Main(string[] args)
{
TcpChannel chnl = new TcpChannel(1241);
ChannelServices.RegisterChannel(chnl);
RemotingConfiguration.RegisterWellKnownServiceType(typeof(SecondaryServer),
"SecondaryServer.tcp", WellKnownObjectMode.SingleCall);
Console.WriteLine("Secondary Remote Server Starting Port:1241 …");
Console.WriteLine("Press <enter> to exit");
Console.ReadLine();
}
}
}
</enter>

The secondary server takes an object created on the primary server and does some work with it.
// The Client
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

using MultiServerLib;

namespace Client
{
class Client
{
[STAThread]
static void Main(string[] args)
{
TcpChannel chnl = new TcpChannel();
ChannelServices.RegisterChannel(chnl);

BaseRemotePrimaryObject brObj = (BaseRemotePrimaryObject) Activator.GetObject(
typeof(BaseRemotePrimaryObject),
"tcp://localhost:1240/PrimaryServer.tcp");

BaseRemoteSecondaryObject brsObj = (BaseRemoteSecondaryObject) Activator.GetObject(
typeof(BaseRemoteSecondaryObject),
"tcp://localhost:1241/SecondaryServer.tcp");

brObj.setValue(910);
Console.WriteLine("Client: Setting PrimaryServerObject to:{0}", brObj.getValue());
brsObj.doWork(brObj);
Console.WriteLine("Client: Setting PrimaryServerObject via SecondaryServer doWork to:{0}",
brObj.getValue());
}
} // class Client
} // namespace Client

The client first creates a remote primary object and a remote secondary object, then makes a remote call on the secondary object with the primary object as a parameter. When the secondary object calls a method on the primary object, no interim message is sent via the client. All messages are sent between the two server objects. Below are some figures showing the multi-server in action.
Here is a diagram of what takes place.
Class Factory Example
The class factory pattern has become a well loved idiom of object-oriented design. The general idea is to generate a class indirectly, have some static method or a seperate class (the class factory) do the generating. In .Net Remoting we can use this pattern to overcome the problem of requiring a non-default constructor for a SAO. The outline is very simple:
// The common assembly
using System;

namespace FactoryLib
{
public abstract class RemoteClass : MarshalByRefObject
{
public abstract int getValue();
public abstract void setValue(int pVal);
}

// The factory class that generates the real class
public abstract class RemoteFactory : MarshalByRefObject
{
public abstract RemoteClass NewInstance(int pInitValue);
}
}

The worker class is the class we want to generate with a non-default constructor, (RemoteClass) the other is the class factory (RemoteFactory) used to generate the worker class instances.
// The Server
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

using FactoryLib;

namespace Server
{
class RemoteServer : RemoteClass
{
int mValue;

public RemoteServer(int pValue)
{
Console.WriteLine("RemoteServer.ctor(int) called");
mValue = pValue;
}

public override int getValue()
{
Console.WriteLine("RemoteServer.getValue() = {0}", mValue);
return mValue;
}

public override void setValue(int pValue)
{
Console.WriteLine("RemoteServer.setValue() old={0} new={1}", mValue, pValue);
mValue = pValue;
}
} // class MyRemoteObject

class RemoteFactoryServer : RemoteFactory
{
public RemoteFactoryServer()
{
Console.WriteLine("RemoteFactory.ctor() called");
}

public override RemoteClass NewInstance(int pInitValue)
{
Console.WriteLine("RemoteServer.NewInstance(int) called");
return new RemoteServer(pInitValue);
}
} // class MyRemoteFactory

class ServerStartup
{
[STAThread]
static void Main(string[] args)
{
Console.WriteLine("Server started on port 1236");
TcpChannel chnl = new TcpChannel(1236);
ChannelServices.RegisterChannel(chnl);

RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemoteFactoryServer),
"remotefactoryserver.tcp", WellKnownObjectMode.Singleton);
/> Console.WriteLine("Press <enter> to exit");
Console.ReadLine();
}
}
}
</enter>

The server must implement both classes, the abstract worker class (inherited by RemoteServer) and the factory class RemoteFactoryServer.
// The Client
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

using FactoryLib;

namespace Client
{
class Client
{
[STAThread]
static void Main(string[] args)
{
TcpChannel chnl = new TcpChannel();
ChannelServices.RegisterChannel(chnl);

RemoteFactory rFact = (RemoteFactory) Activator.GetObject(typeof(RemoteFactory),
"tcp://localhost:1236/remotefactoryserver.tcp");

RemoteClass obj1 = rFact.NewInstance(892);
Console.WriteLine("obj1.getValue()= {0})", obj1.getValue());
}
}
}

The client creates an instance of the RemoteFactory and uses it to generate a RemoteClass with a non-default constructor. Here are some shots of the class factory example.

Viewing all articles
Browse latest Browse all 10

Trending Articles