.NET Remoting (or “what the cool kids are NOT doing”)
Today, in a fit of insanity curiosity, I decided to find out how hard it would be to communicate between two CLR applications using .NET remoting. This is how far behind the bleeding edge I am — all the cool kids are using WCF, web services or what have you.
Nevertheless, the scenario is simple: I want to have two console applications, one of which should display messages sent from the other. This is how I went about it:
First, create an assembly that contains the class of the object we want to share between applications. I opted to call the class RemoteEndpoint. The docs say it needs to inherit MarshalByRefObject, so that’s what I’ll make it do then. Also, because events are nice, I’m defining a delegate type and a MessageReceivedEventArgs type for passing things around:
using System; namespace CommonTypes { public class MessageReceivedEventArgs : EventArgs { public MessageReceivedEventArgs(string message) { Message = message; } private string m_message; public string Message { get { return m_message; } set { m_message = value; } } } public delegate void MessageReceivedEvent(MessageReceivedEventArgs args); public class RemoteEndpoint : MarshalByRefObject { public RemoteEndpoint() { Console.WriteLine("Ctor called, ready to receive remote calls."); } public event MessageReceivedEvent MessageReceved; public void ReceiveMessage(string message) { if(MessageReceved == null) return; MessageReceved(new MessageReceivedEventArgs(message)); } } }
I then created two console applications, and referenced the project containing the above types and the BCL System.Runtime.Remoting in both of them.
Next up, the console apps themselves. Here’s Recipient:
using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using CommonTypes; namespace Recipient { public class Program { [STAThread] public static void Main(string[] args) { ChannelServices.RegisterChannel(new TcpChannel(1234), false); RemoteEndpoint endPoint = new RemoteEndpoint(); endPoint.MessageReceved += Program_MessageReceved; RemotingServices.Marshal(endPoint, "RemoteEndpoint"); Console.WriteLine("Hit enter to exit"); Console.ReadLine(); } static void Program_MessageReceved(MessageReceivedEventArgs args) { Console.WriteLine(args.Message); } } }
Not rocket science. All we need to do is register a TCP channel, specify a port for it, create the object we want to share and then RemotingServices.Marshal() it with a URI we can use at the other end. Speaking of which:
using System; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using CommonTypes; namespace Sender { public class Program { public static void Main(string[] args) { ChannelServices.RegisterChannel(new TcpChannel(), false); RemoteEndpoint endPoint = (RemoteEndpoint) Activator.GetObject(typeof (RemoteEndpoint), "tcp://localhost:1234/RemoteEndpoint"); Console.WriteLine("Hit enter to send a message. Q to stop."); string line = Console.ReadLine(); while(line.Trim().ToLowerInvariant() != "q") { Console.WriteLine("Sending a message."); endPoint.ReceiveMessage(line); line = Console.ReadLine(); } Console.WriteLine("So long, and thanks for all the fish!"); } } }
Here we instantiate the remote object by way of Activator.GetObject. Since it’s published under the name “RemoteEndpoint” on a TcpChannel on localhost, port 1234, the URI is tcp://localhost:1234/RemoteEndpoint. Now, every time the user hits Enter, if the line isn’t the magic “Q”, the line gets sent to the remote recipient.
Mandatory note: this is just me learning a new trick, not production quality code.
Edit: As evidenced by the fact that I had written:
while(line.Trim().ToLowerInvariant() != "Q")
Gotta love them lowercase Q’s. (Thanks for pointing that out to me, J 🙂