Server -II-

Feynman_8

In my previous post I announced that I want to try covering one feature in each post. During the days I doing this, I realized this would not work for me. I feel better in posting more frequent and discussing the code in smaller chunks.  In this post I want to start creating the basic server application.

Part 1: Service Host

The first thing I had to deal with was the decision how to host the service. As I mentioned in previous post I started with WCF cause at this time decided to focus my effort to come closer to my goal and not learning messaging pattern and different libraries.

But now, lets discuss how to host a WCF service for a trading application using request / response messaging. After trying some stuff for learning, I started thinking about a concept how to host the service and manage the messaging. An important point before starting the design is to chose how the service becomes managed. That means how do we handle configurations about the host, endpoints e.g. IP address, ports, and all other modules attached to the service later e.g. datafeeds, clients, … I decided to have a simple Form to do this because it was more convenient to me at this time.

hostConcept#

So we have a WinForm which provides all settings and starts the WCF Service Host instance. That’s how the server works in general. This brings us to the WCF Service Library project, I had created. Lets name it WcfServiceHost. This project will contain one interface and two classes only. That’s all we need to host the service!

First of all lets care about the interface “IWCFService.cs” where the core functionality of hosting is provided.

using System;
using System.ServiceModel;
using ServiceAuxiliaries;

namespace WcfServiceHost
{
    public interface IWCFOutgoingComm
    {
        [OperationContract(IsOneWay = true)]
        void MessageToClient(ResponseMessage message);
    }

    [ServiceContract(CallbackContract = typeof(IWCFOutgoingComm), Name = "IWCFService", SessionMode = SessionMode.Required)]
    public interface IWCFService
    {
        [OperationContract(IsInitiating = true)]
        [FaultContract(typeof(ServerException))]
        LoginResponse Login(LoginRequest message);

        [OperationContract(IsTerminating = true, IsInitiating = false, IsOneWay = true)]
        void LogOut();

        [OperationContract(IsInitiating = false, IsOneWay = true)]
        void MessageIncoming(RequestMessage message);
    }

    public class WCFClientSession : MarshalByRefObject, IClientSession
    {
        public IWCFOutgoingComm CallBack;
        public OperationContext Context;
        public IContextChannel Channel;

        public WCFClientSession(string aLogin, string aSessionId, OperationContext aCtx)
        {
            Login = aLogin;
            ID = aSessionId;
            Context = aCtx;
            CallBack = aCtx.GetCallbackChannel<IWCFOutgoingComm>();
            Channel = aCtx.Channel;
            SessionObject = aCtx.Channel;
        }

        public string ID { get; set; }

        public string Login { get; set; }

        public object SessionObject { get; set; }

        public void Disconnect()
        {
            Send(new HeartbeatResponse("close session"));
            Channel.Close();
        }

        public void Heartbeat()
        {
            Send(new HeartbeatResponse(string.Empty));
        }

        public void Send(ResponseMessage aResponse)
        {
            CallBack.MessageToClient(aResponse);
        }

        public void SendError(Exception e)
        {
            CallBack.MessageToClient(new ErrorInfoResponse(e));
        }

    }
}

Its noticeable that IWCFService contains another interface IWCFOutgoingComm which provides the implementation of all response messages send to client direction. The class WCFClientSession establishes the connection to the service from any client. Also it provides a basic set of callbacks send to client by using IWCFOutgoingComm.

 

Now lets define a class WCFService which implements the IWCFService interface. Additionally some methods for start and stop are included as well some helper methods for parsing the client session id for clean logout mechanics.

using System;
using System.ServiceModel;
using ServiceAuxiliaries;

namespace WcfServiceHost
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
    public class WCFService : IWCFService
    {
        /// starts service
        public void Start()
        {
        }

        /// stops service
        public void Stop()
        {
        }
        #region IWCFService Interface
        /// sends incoming message to message router
        public void MessageIncoming(RequestMessage aRequestMessage)
        {
            MessageRouter.gMessageRouter.ProcessRequest(GetSessionID(OperationContext.Current.SessionId), aRequestMessage);
        }

        /// implements functionalities: authenticate, initialize user/session info, add user/session info
        public LoginResponse Login(LoginRequest aRequestMessage)
        {
            var aResponse = new LoginResponse();

            try
            {
                var loggedIn = MessageRouter.gMessageRouter.Authenticate(aRequestMessage.Login, aRequestMessage.Password);
                if (loggedIn)
                {
                    var aUserInfo = new WCFClientSession(aRequestMessage.Login, GetSessionID(OperationContext.Current.SessionId), OperationContext.Current);
                    lock (MessageRouter.gMessageRouter)
                    {
                        MessageRouter.gMessageRouter.AddSession(aUserInfo.ID, aUserInfo);
                        aUserInfo.Channel.Closed += Chanel_Closed;
                        aUserInfo.Channel.Faulted += Chanel_Closed;
                    }
                }
                else
                {
                    throw new ApplicationException("Logon fault.");
                }
            }
            catch (Exception ex)
            {
                throw new FaultException<ServerException>(new ServerException(ex.Message), new FaultReason(ex.Message));
            }
            return aResponse;
        }

        public void LogOut()
        {
            if (OperationContext.Current == null)
                return;

            string aID = OperationContext.Current.SessionId;

            try
            {
                WCFClientSession aClientSessionInfo;

                lock (MessageRouter.gMessageRouter)
                {
                    aClientSessionInfo = MessageRouter.gMessageRouter.RemoveSession(GetSessionID(aID)) as WCFClientSession;
                    if (aClientSessionInfo != null)
                    {
                        aClientSessionInfo.Channel.Closed -= Chanel_Closed;
                        aClientSessionInfo.Channel.Faulted -= Chanel_Closed;
                    }
                }
            }
            catch (Exception)
            {
            }
        }
        #endregion

        #region Helper Methods
        private string GetSessionID(string aID)
        {
            string[] arr = aID.Split(new char[] { ':', ';' });

            return arr[1] + ":" + arr[2];
        }

        /// occurs when WCF session closed or failed
        void Chanel_Closed(object sender, EventArgs e)
        {
            try
            {
                lock (MessageRouter.gMessageRouter)
                {
                    IClientSession aClientSessionInfo = MessageRouter.gMessageRouter.GetClientSessionInfo(sender);

                    if (aClientSessionInfo != null)
                    {
                        MessageRouter.gMessageRouter.RemoveSession(aClientSessionInfo.ID);
                    }
                }
            }
            catch (Exception)
            {
            }
        }
        #endregion
    }
}

 

Last but not least we need a class to get control over the WCF configurations. This class named WCFServiceHost.cs implements an interface IServiceHost, which is part in a later post.

using System;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Collections.Generic;
using ServiceAuxiliaries;
using ServiceAuxiliaries.Interfaces;

namespace WcfServiceHost
{
    /// WCF connection service host
    public class WCFServiceHost : IServiceHost
    {
        private WCFService m_ServiceCore;
        private ServiceHost m_ServiceHost;

        #region IServiceHost implementation

        public Dictionary<string, object> DefaultSettings
        {
            get
            {
                Dictionary<string, object> aDict = new Dictionary<string, object>();

                aDict["ip"] = "127.0.0.1";
                aDict["port"] = "4504";

                return aDict;
            }
        }

        public string Name
        {
            get
            {
                return "WCF";
            }
        }

        public void Start(Dictionary<string, object> args)
        {
            //configure wcf
            string defaultIP = null;
            int port = 0;
            int design_time_port = 0;

            if (args.ContainsKey("ip"))
                defaultIP = args["ip"] as string;
            if (defaultIP == null)
                throw new Exception("WCF session manager can not start because 'ip' parameter is not specified");
            if (args.ContainsKey("port"))
                Int32.TryParse(args["port"].ToString(), out port);
            if (port == 0)
                throw new Exception("WCF session manager can not start because 'port' parameter is not specified");
            if (args.ContainsKey("design_time_port"))
                Int32.TryParse(args["design_time_port"].ToString(), out design_time_port);

            TimeSpan timeOut = new TimeSpan(0, 1, 0);
            List<Uri> aURIs = new List<Uri>();

            aURIs.Add(new Uri(string.Format("net.tcp://{0}:{1}/Server_Service", defaultIP, port)));
            if (design_time_port != 0)
                aURIs.Add(new Uri(string.Format("http://{0}:{1}/Server_Service", defaultIP, design_time_port)));

            NetTcpBinding binding = new NetTcpBinding();
            binding.TransactionFlow = false;
            binding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;
            binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows;
            binding.ReceiveTimeout = timeOut;
            binding.SendTimeout = timeOut;
            binding.OpenTimeout = timeOut;
            binding.CloseTimeout = timeOut;
            binding.ReliableSession.InactivityTimeout = timeOut;
            binding.Security.Mode = SecurityMode.None;
            binding.MaxBufferSize = 1073741823;
            binding.MaxReceivedMessageSize = 1073741823;
            binding.MaxBufferPoolSize = 1073741823;

            // start WCF service
            m_ServiceCore = new WCFService();
            m_ServiceCore.Start();
            m_ServiceHost = new ServiceHost(m_ServiceCore, aURIs.ToArray());
            m_ServiceHost.AddServiceEndpoint(typeof(IWCFService), binding, aURIs[0]);
            m_ServiceHost.Description.Behaviors.Add(new ServiceMetadataBehavior());
            m_ServiceHost.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
            m_ServiceHost.Open();
        }

        public void Stop()
        {
            if (m_ServiceCore != null)
            {
                m_ServiceCore = null;
            }
            if (m_ServiceHost != null)
            {
                if (m_ServiceHost.State == CommunicationState.Opened)
                {
                    try
                    {
                        m_ServiceHost.Close();
                    }
                    catch (Exception e)
                    {
                    }
                }
                m_ServiceHost = null;
            }
        }

        public ILogonControl LogonControl
        {
            get
            {
                return new LogonControl();
            }
        }
        #endregion
    }
}

This class provides some default settings for the host object and finally creates the instance of the WCF Service.

This was about the service host. Next I will talk about, and add, another class library to the solution. This new class library will handle all service related auxiliary classes, interfaces and message types. Many gaps from the current WCFServiceHost class library were closed after next post. If you have some comments don’t hesitate to contact me. To get a preview about upcoming topics, please have a look here: Content++.

Feynman_5

quantocracy-badge-130

 

Advertisements

2 thoughts on “Server -II-

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s