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