Remoting (part 2, advanced), WCF
WCF improves code concepts substantially. There are templates like “WCF Service Application” in Visual Studio making programming extremely easy. You get results with a few clicks.
I am concentrating on an independent code solution today. This basically is a similar solution to my remoting example in part 1. You can clearly see the differences rather than starting an entirely new project type.
We need two App.config files.
One on the client side
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <client> <endpoint name="ServerInstance" address="net.tcp://localhost/ServerInstance" binding="netTcpBinding" bindingConfiguration="tcp_Unsecured" contract="Shared.MyInterface"/> </client> <bindings> <netTcpBinding> <binding name="tcp_Unsecured"> <security mode="None" /> </binding> </netTcpBinding> </bindings> </system.serviceModel> </configuration>
and one on the server-side.
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <services> <service name="Server.ServerInstance"> <endpoint address="net.tcp://localhost/ServerInstance" binding="netTcpBinding" bindingConfiguration="tcp_Unsecured" contract="Shared.MyInterface" /> </service> </services> <bindings> <netTcpBinding> <binding name="tcp_Unsecured"> <security mode="None" /> </binding> </netTcpBinding> </bindings> </system.serviceModel> </configuration>
I guess you have already noticed the Shared.MyInterface. This is the same approach as in the classical Remoting. We share data definitions.
using System; using System.Collections.Generic; using System.ServiceModel; namespace Shared { [ServiceContract(SessionMode = SessionMode.Allowed)] public interface MyInterface { [OperationContract] List<TradeData> GetTrades(); [OperationContract] string HelloWorld(string xName); [OperationContract] DateTime Ping(); } // interface } // namespace
The shared area also needs our TradeData class, which is used inside the Interface. Do not forget the Serializable attribute. You do not necessarily get an error message. The program can simply refuse proper execution and only provide mind-boggling error messages.
using System; namespace Shared { [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 } // namespace
The class ServerInstance is part of the server-side. It inherits MyInterface and adds the required statements of MyInterface.
using System; using System.Collections.Generic; using System.ServiceModel; using Shared; namespace Server { [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] internal class ServerInstance : MyInterface { public List<TradeData> GetTrades() { DateTime lDummyTime = new DateTime(2014, 02, 12, 16, 30, 10); TradeData lTrade1 = new TradeData(lDummyTime, "DTE", 11.83, 100); TradeData lTrade2 = new TradeData(lDummyTime.AddSeconds(2), "DAI", 66.45, 300); TradeData lTrade3 = new TradeData(lDummyTime.AddSeconds(5), "DBK", 35.91, 100); return new List<TradeData>() { lTrade1, lTrade2, lTrade3 }; } // public string HelloWorld(string xName) { return "Hello " + xName; } // public DateTime Ping() { return DateTime.Now; } // } // class } // namespace
The server side code is relatively short. It is more or less a two liner to instantiate a ServiceHost and open it.
using System; using System.ServiceModel; using System.Threading; namespace Server { public class Program { public static void Main(string[] arguments) { Thread.Sleep(10000); // we let the client wait 10 seconds to simulate a server that is down using (ServiceHost lServiceHost = new ServiceHost(typeof(ServerInstance))) { lServiceHost.Open(); Console.WriteLine("Service is available."); Console.ReadKey(); } } // main } // class } // namespace
And the client side is not much longer. Have a look for yourself. Most of the lines are taking care of printing the data to the console window.
using System; using System.ServiceModel; using System.ServiceModel.Channels; using System.Threading; using Shared; namespace Client { public class Program { public static void Main(string[] args) { ChannelFactory<MyInterface> lFactory = new ChannelFactory<MyInterface>("ServerInstance"); MyInterface lShared = null; while (true) { try { lShared = lFactory.CreateChannel(); 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); break; } catch (Exception ex) { Console.WriteLine(ex.Message); Thread.Sleep(2000); } finally { IChannel lChannel = lShared as IChannel; if (lChannel.State == CommunicationState.Opened) lChannel.Close(); } } Console.ReadKey(); } // main } // class } // namespace
After starting the client and server concurrently,
you should get output results like these:
SERVER example output:
Service is available.
CLIENT example output:
Could not connect to net.tcp://localhost/ServerInstance. The connection attempt
lasted for a time span of 00:00:02.0521174. TCP error code 10061: No connection
could be made because the target machine actively refused it 127.0.0.1:808.
Could not connect to net.tcp://localhost/ServerInstance. The connection attempt
lasted for a time span of 00:00:02.0391166. TCP error code 10061: No connection
could be made because the target machine actively refused it 127.0.0.1:808.
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(): 17/02/2014 12:30:59
The error messages on the client side are as expected. They are caused by “Thread.Sleep(10000);” on the server side. I thought it would be interesting to show a server which is not running at the time when starting the client.
Put aside all the complex and confusing examples on the internet. This one here is spot on. It provides a nice and comfortable entry point into WCF. The code does not have to be complex.
I recommend YouTube for further WCF explanations.
Posted on February 18, 2014, in Advanced, C#, Network, Remoting, WCF, XML and tagged C#, C-sharp, old school, programming, Remoting, Serialization, Source code, WCF, XML. Bookmark the permalink. Leave a comment.
Leave a comment
Comments 0