.NET Remoting is more versatile and extensible in terms of enabling communication between objects using different transport protocols and serialization formats. TCP, HTTP, and custom protocols are supported as are Binary, SOAP, and custom formats. Multiple-object creation and lifetime modes are supported including Singleton, SingleCall, and Client-Activated. Remoting requires a host process, which can be IIS, or a Microsoft® Windows service or executable written in .NET.
Introduction
A .NET Remoting object using the SOAP formatter can be exposed as an XML Web service when hosted in IIS with ASP.NET. Since the payload is encoded in SOAP over HTTP, any client that supports the SOAP Encoding format can access the objects over the Internet. Another advantage of using the HTTP protocol is that it generally passes unobstructed across most firewalls. The TCP channel and the Binary formatter can be employed in an intranet environment where both the server and client are running on the .NET Framework. This approach is optimized for performance since raw sockets are used to transmit data across the network using a custom protocol that is more efficient than HTTP. Though this approach offers excellent performance in a closed environment, it cannot be employed in cross-platform scenarios where the client is not running the .NET Framework.
IIS hosting provides secure communication for wire-level protection using Secure Sockets Layer (SSL), and you can also leverage IIS and ASP.NET authentication and authorization. The TCP channel as well as the HTTP channel with non-IIS hosting do not support secure socket transmission, so data is transmitted in clear text. You are required to implement your own security if you are using the TCP channel or the HTTP channel hosted in processes other than IIS.
Reference :
http://msdn.microsoft.com/en-us/library/ms978411.aspxAsynchronous 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.
using System;
namespace BaseLib
{
public abstract class BaseRemoteObject : MarshalByRefObject
{
public abstract void setValue(int pValue);
public abstract int getValue();
public abstract string getText();
}
}
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 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 to exit");
Console.ReadLine();
}
}
}
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);
}
}
}
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:
using System;
namespace MultiServerLib
{
public abstract class BaseRemotePrimaryObject : MarshalByRefObject
{
public abstract void setValue(int pValue);
public abstract int getValue();
}
public abstract class BaseRemoteSecondaryObject : MarshalByRefObject
{
public abstract void doWork(BaseRemotePrimaryObject brObj);
}
}
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 to exit");
Console.ReadLine();
}
}
}
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 to exit");
Console.ReadLine();
}
}
}
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 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:
using System;
namespace FactoryLib
{
public abstract class RemoteClass : MarshalByRefObject
{
public abstract int getValue();
public abstract void setValue(int pVal);
}
public abstract class RemoteFactory : MarshalByRefObject
{
public abstract RemoteClass NewInstance(int pInitValue);
}
}
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 to exit");
Console.ReadLine();
}
}
}
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());
}
}
}