Daily Archives: February 13, 2014
Remoting (part 1, basics), old school
Before I come to the new WCF style Remoting, I would like to introduce the good old System.Runtime.Remoting namespace.
Remoting is a convenient way to call methods across the network. You don’t have to write messages to trigger methods on the server, wait for results and then analyse feedback messages on the client side. What could be easier than calling server methods directly?
The downside is the slowness and the missing encryption. Nevertheless, if you don’t have many server requests then Remoting is probably the right solution for you. The ease is hard to beat.
We need a library to share the syntax between the server and client. In practice you would compile the following code into a library and implement it on both sides. Don’t be lazy and only share the source code. This cannot work, because two compilers generate two different system object IDs.
In today’s example we are running the code in one Visual Studio process. We are also using the localhost. This is why we do not need an external library on both sides. We are using the same compiler object ID.
The class MarshalByRefObject enables Remoting access to objects across application domain boundaries. We need to inherit this class.
public abstract class RemotingShared : MarshalByRefObject { public const string RemotingName = "MyRemotingName"; public const string ServerIpAddress = "127.0.0.1"; public const int Port = 65432; [Serializable] public class TradeData { public DateTime tradeTime; public string ticker; public double price; public int quantity; public override string ToString() { return tradeTime.ToString("dd.MMM.yy HH:mm:ss ") + ticker + " " + quantity + " @ " + price + " EUR"; } // public TradeData(DateTime xTradeTime, string xTicker, double xPrice, int xQuantity) { tradeTime = xTradeTime; ticker = xTicker; price = xPrice; quantity = xQuantity; } // constructor } // class public abstract List<TradeData> GetTrades(); public abstract string HelloWorld(string xName); public abstract DateTime Ping(); } // class
The server overrides methods of the abstract class RemotingShared by inheriting it. We do not need to create any instance. This is done in the background by the Remoting process. The option WellKnownObjectMode.Singleton makes sure that only one instance will be created and reused. WellKnownObjectMode.SingleCall would create new instances for each incoming message.
public class RemotingSharedDerived : RemotingShared { public override List<RemotingShared.TradeData> GetTrades() { DateTime lDummyTime = new DateTime(2014, 02, 12, 16, 30, 10) ; RemotingShared.TradeData lTrade1 = new TradeData(lDummyTime, "DTE", 11.83, 100); RemotingShared.TradeData lTrade2 = new TradeData(lDummyTime.AddSeconds(2), "DAI", 66.45, 300); RemotingShared.TradeData lTrade3 = new TradeData(lDummyTime.AddSeconds(5), "DBK", 35.91, 100); return new List<TradeData>() { lTrade1, lTrade2, lTrade3 }; } // public override string HelloWorld(string xName) { return "Hello " + xName; } // public override DateTime Ping() { return DateTime.Now; } // } // class public static void StartServer() { TcpChannel lTcpChannel = new TcpChannel(RemotingShared.Port); ChannelServices.RegisterChannel(lTcpChannel, true); Type lRemotingSharedType = typeof(RemotingSharedDerived); RemotingConfiguration.ApplicationName = RemotingShared.RemotingName + "App"; RemotingConfiguration.RegisterWellKnownServiceType(lRemotingSharedType, RemotingShared.RemotingName, WellKnownObjectMode.Singleton); } //
Let’s have a look at the client now. As usual I kept the code as short as possible. Personally I do not like example programs that include too much redundant information, they can be quite confusing sometimes.
As we are on the localhost, the server has registered a ‘tcp’ channel already. We check if it exists although we already know it does. But the example program would throw an exception otherwise. Keep it in the code, it does make sense when you are remoting between two different IP addresses.
public static void StartClient() { string lPath = "tcp://" + RemotingShared.ServerIpAddress + ":" + RemotingShared.Port + "/" + RemotingShared.RemotingName; TcpChannel lTcpChannel = new TcpChannel(); if (!ChannelServices.RegisteredChannels.Any(lChannel => lChannel.ChannelName == lTcpChannel.ChannelName)) { ChannelServices.RegisterChannel(lTcpChannel, true); } RemotingShared lShared = (RemotingShared)Activator.GetObject(typeof(RemotingShared), lPath); var o = lShared.GetTrades(); var s = lShared.HelloWorld("Mr. Ohta"); var p = lShared.Ping(); Console.WriteLine("GetTrades():"); foreach (var lTrade in o) Console.WriteLine(lTrade.ToString()); Console.WriteLine(); Console.WriteLine("HelloWorld(): " + s); Console.WriteLine(); Console.WriteLine("Ping(): " + p); } //
Let’s run the code now.
public static void Test() { RemotingServer.StartServer(); RemotingClient.StartClient(); Console.WriteLine("\nPress any key to exit."); Console.ReadKey(); } //
example output:
GetTrades():
12.Feb.14 16:30:10 DTE 100 @ 11.83 EUR
12.Feb.14 16:30:12 DAI 300 @ 66.45 EUR
12.Feb.14 16:30:15 DBK 100 @ 35.91 EURHelloWorld(): Hello Mr. Ohta
Ping(): 12/02/2014 20:36:55
Press any key to exit.