IT Forum >> Tutorial: .NET Remoting
| 6/19/05 6:10 PM | |
theseanster
157
Edited: 19-Jun-05 Member Since: 05/13/2002 Posts: 7491 |
Remoting and CallbacksFor those of you who don't know what .NET remoting is, it is basically like DCOM with a lot more flexibility built in. Unlike the Win32 world, when a process spins up for managed code there is something called an AppDomain which is sort of like a process within a process. A Win32 process can contain 1 or more AppDomains. Remoting essentially lets two components from different AppDomains talk to each other. This is regardless of whether each component is on the same physical machine or across the network. The following example code will show you how to create a remote object using a binary format over TCP. This is the fastest. For demo purposes, I'm hosting each application in a console. Typically you need some kind of host (or "daemon") like a Windows Service or IIS to host a remoting object. To use IIS, however, you have to run over HTTP instead of TCP (SOAP over HTTP). The nice thing is that IIS gives you the easy of hosting along with its security features, whereas if you implement binary over TCP, you are probably going to host the remoting server object in a Windows Service memory space and have to manually secure it by rolling your own security even sinks or using something like IPSec. There are actually two examples going on here...one is remoting, the other is the fact that the remoting call is done asynchronously to avoid blocking the client. Hopefully this doesn't overcomplicate the example, but you should be able to distinguish the two concepts fairly easily. In order for a remoted server object to perform a callback to the client, they have to reside on the same network. So, I went for kind of a "wow factor" but keep in mind that if your clients are behind a firewall, this callback example is essentially worthless. Although I would question why you are using remoting over a firewall instead of using ASMX, but that's another topic. So, the approach I usually take with remoting is to create a common assembly that contains the "contracts" for the functionality that the server performs. I hate Soapsuds.exe, and I hate when people directly reference the server to get the metadata because quite frankly you never know if you are remoting or loading the server directly into your own AppDomain! So, shared interfaces and/or base classes is the best IMHO, so that's the route I'm going. The Shared Assembly: TaskManagement.dll The shared assembly defines an interface for the operation that the remote server object will adhere to. This assembly is simply a class library. You basically see an event being defined and a method that takes no parameters. Additionally you see a "shim" for the callback. This shim is necessary to avoid the requirement of the server needing to have the metadata for each client in order to make the callback. That is, our client needs to know where the server resides to make the call, but...if we create a shim to manage the remoting of delegates, the server only needs to know about the shim, not the client. The shim knows about the client and forwards the communications to the client. Notice that you will be seing MarshalByRefObject...all remoted objects must inherit from this class. |
| 6/19/05 6:12 PM | |
theseanster
157
Edited: 19-Jun-05 06:12 PM Member Since: 05/13/2002 Posts: 7492 |
using System;
namespace TaskManagement { public delegate void JobFinishedDelegate(long confirmationNumber);
public interface IJob { void Start(); event JobFinishedDelegate JobFinished; }
public class CallBackShim : MarshalByRefObject { private JobFinishedDelegate target;
private CallBackShim(JobFinishedDelegate target) { this.target += target; }
public void JobFinishedShim(long confirmationNumber) { target(confirmationNumber); }
public static JobFinishedDelegate Create(JobFinishedDelegate target) { CallBackShim shim = new CallBackShim(target); return new JobFinishedDelegate(shim.JobFinishedShim); } } } |
| 6/19/05 6:12 PM | |
theseanster
157
Edited: 19-Jun-05 06:13 PM Member Since: 05/13/2002 Posts: 7493 |
The Server code The Server is basically just a console application that fires up an instance of a class the conforms to the interface defined in the shared library. Notice the configuring remoting services to host the objects on a particual TCP port. Pick any you like. There are a few unusual tricks going on in the Console app code, main the TypeFilterLevel.Full being set in a BinaryServerFormatterSinkProvider object. This is basically a security override to allow me to pass a delegate through AppDomains. If not, it could be a security vulnerability because a delegate encapsulates a method call and some bad guy could put in some other method that conforms to the delegate's definition. Other than that, I'm just registering a TCP channel and registering the object type as being available to remote as a Singleton. A singleton is a design pattern where you get only one instance of a particular object that is shared by everyone who calls it. Another type of server-activated remoting object is SingleCall, which means you have a stateless object - you get a new instance on every method call so having a conversation with one of those is rather difficult. Singleton allows us to do callbacks. The server class LongRunningJob will asynchronously fire off a job by calling it's own private delegate using BeginInvoke. It basically simulates a long running job for 10 seconds, calls back to the client and cleans up the delegate gue using EndInvoke.
|
| 6/19/05 6:19 PM | |
theseanster
157
Edited: 19-Jun-05 Member Since: 05/13/2002 Posts: 7494 |
using System; using System.Collections; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using System.Runtime.Serialization.Formatters; using System.Threading; using TaskManagement;
namespace RemotingServer { public class LongRunningJob : MarshalByRefObject, IJob { public event JobFinishedDelegate JobFinished;
private delegate void RunJobDelegate();
private void RunJob() { //simulate long running task for 10 seconds Thread.Sleep(10000); }
public void Start() { AsyncCallback cb = new AsyncCallback(this.CallBack); RunJobDelegate del = new RunJobDelegate(this.RunJob); del.BeginInvoke(cb, del); }
private void CallBack(IAsyncResult ar) { if (null != JobFinished) { try { JobFinished(DateTime.Now.Ticks); } catch (Exception e) { Console.WriteLine("Failed to fire event."); Console.WriteLine(e.Message); throw e; } }
if (ar != null && ar.AsyncState != null && ar.AsyncState is RunJobDelegate) { ((RunJobDelegate) ar.AsyncState).EndInvoke(ar); } } }
|
| 6/19/05 6:20 PM | |
theseanster
157
Edited: 19-Jun-05 Member Since: 05/13/2002 Posts: 7495 |
public class Server { [STAThread] private static void Main(string[] args) { Hashtable options = new Hashtable(1); options.Add("port", 8084); BinaryServerFormatterSinkProvider provider = new BinaryServerFormatterSinkProvider(); provider.TypeFilterLevel = TypeFilterLevel.Full; TcpChannel channel = new TcpChannel(options, null, provider);
ChannelServices.RegisterChannel(channel); RemotingConfiguration.RegisterWellKnownServiceType(typeof (LongRunningJob), "LongRunningJob.rem", WellKnownObjectMode.Singleton);
Console.WriteLine("Press ENTER to quit"); Console.ReadLine(); } } } |
| 6/19/05 6:20 PM | |
theseanster
157
Edited: 19-Jun-05 Member Since: 05/13/2002 Posts: 7496 |
The Client Code The client application is probably the easiest to read. Typically you don't even put all of the remoting configuration crap in the code. Instead you put it all in a config file and then simply call a single RemotingConfiguration method to load it all. In this case I am registering channel zero just for the callback, and then late binding to an remoting object hosted on the server via its TCP port using Activator.GetObject. I call a factory method (hey wow, another design pattern) to get back a delegate to subscribe to the server event for a callback. Then I fire the Start method and wait. That's it!
|
| 6/19/05 6:21 PM | |
theseanster
157
Edited: 19-Jun-05 Member Since: 05/13/2002 Posts: 7497 |
using System; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using TaskManagement;
namespace RemotingClient { public class ClientApp { [STAThread] private static void Main(string[] args) { TcpChannel channel = new TcpChannel(0); ChannelServices.RegisterChannel(channel);
IJob job = (IJob) Activator.GetObject(typeof (IJob), "tcp://localhost:8084/LongRunningJob.rem");
JobFinishedDelegate cb = CallBackShim.Create(new JobFinishedDelegate(job_OnJobFinished)); job.JobFinished += cb; job.Start();
Console.WriteLine("Please wait..."); Console.ReadLine(); }
private static void job_OnJobFinished(long confirmationNumber) { Console.WriteLine("Job finished. Confirmation {0}", confirmationNumber); Console.WriteLine("Please ENTER to exit"); } } } |
| 6/26/05 12:14 AM | |
theseanster
157
Edited: 26-Jun-05 Member Since: 05/13/2002 Posts: 7575 |
Guess this one was a snoozer, huh? :-) |
| 6/26/05 2:04 PM | |
deepu
3
Edited: 26-Jun-05 Member Since: 01/01/2001 Posts: 8181 |
This is awesome! Thank you. |
| 6/26/05 2:18 PM | |
theseanster
157
Edited: 26-Jun-05 Member Since: 05/13/2002 Posts: 7579 |
Can you write me a VB app that would seperate my animal porn from my scat porn? I thought rob was writing you that animal porn app to separate by kingdom, phylum, class, order, genus, species? * can't believe I remember that list from grade school * |
| 6/26/05 3:48 PM | |
|
Andrew Yao
Edited: 26-Jun-05 Member Since: 01/01/2001 Posts: 2829 |
family goes between order and genus. |
| 6/26/05 4:05 PM | |
theseanster
157
Edited: 26-Jun-05 Member Since: 05/13/2002 Posts: 7589 |
Dammit, now we have to tell Rob to rewrite his sorting algorithm! |
| 1/27/06 9:24 PM | |
theseanster
157
Edited: 27-Jan-06 Member Since: 05/13/2002 Posts: 9518 |
TTT by request... |
| 1/28/06 12:13 AM | |
|
Revolver of Reason
Edited: 28-Jan-06 Member Since: 01/01/2001 Posts: 29678 |
I keeel you ded. D-E-D DED! |
| 1/28/06 12:20 AM | |
theseanster
157
Edited: 28-Jan-06 Member Since: 05/13/2002 Posts: 9532 |
I just like the word "shim". Sounds all techy and stuff |
| 11/13/12 11:42 AM | |
theseanster
157
Member Since: 5/13/02 Posts: 21204 |
bump this is more advanced, and from .NET 1.0. I will update this to use WCF using the 4.0 or 4.5 if anyone is interested |
Reply Post
You must log in to post a reply. Click here to login.



