一步步实现自己的框架系列(三):客户端服务端通信的实现

  离上次发表文章已经挺久的了,wcf这块确实挺烦人的,虽然用过几次,但是手写起来还是有点陌生,看了不少wcf的文章,终少有领悟,这里我捎带提起,更详细的我会推荐几篇不错文章供大家参考。

  首先看下wcf大概包括些什么内容,这里是让大家有个清晰的纲领,不会深入介绍wcf,再说这一块也不是我这个凡人能给大家三言两语就能说明白的。

  要使用wcf通信,首先要定义契约,我们再来看下wcf有哪些契约。

  wcf的四种契约,我给他按使用的优先级拍个序吧,Service Contract是必不可少的也是必须的,Data Contract 也是常会用到的,用来定义通信结构体,但是没有这个,我们使用基础类型也是能完成通信,其次是Fualt Contract通信失败的契约,这个能告诉我们调用失败的详细信息也是很重要的,最后是Message Contract,这个是最不常用的了,本文也不会用到,有兴趣的可以自己找点资料看看。

  好吧,引出我们今天的话题,客户端服务端通信的实现,关于通信设计模型大家可以看我第一篇的介绍,这里就不重复了。不管是客户端调用服务端还是服务端回调客户端,都是方法的调用,无非就是:(请求-回复)或者(请求),既然这样看下我定义的数据契约吧,定义的数据契约包括 请求数据契约 和 响应契约 两种。

    [DataContract(Namespace = "http://www.cnblogs.com/guanglin/", Name = "Request")]
    public class Request
    {
        [DataMember]
        public string InstanceId { get; set; }

        [DataMember]
        public string MethodName { get; set; }

        [DataMember]
        public string[] ParamTypes { get; set; }

        [DataMember]
        public byte[] Parameters { get; set; }

    }

  这个是请求契约其中MethodName为请求调用的方法名称,ParamType为请求调用的参数类型,Parameters为请求调用的参数,这里Parameters为什么使用byte[]数据类型呢?因为我们调用的方法参数的类型是不确定的,这里将参数序列化后传输,使用时再反序列化回来即可。这里还有个InstanceId是干什么用的呢,这跟我们的设计有关,我的客户端与服务端是基于页面创建的,这个InstanceId代表的是请求的页面服务或回调页面的唯一标识。

  接下来看下响应契约,这个就简单了。

    [DataContract(Namespace = "http://www.cnblogs.com/guanglin/", Name = "Response")]
    public class Response
    {
        [DataMember]
        public string InstanceId { get; set; }

        [DataMember]
        public byte[] Value { get; set; }
    }

  回复契约只有唯一标识和返回值,并且返回值也使用byte[]传输,大家也能想到,这里返回值也将使用序列化传输。

  好吧,数据契约定义完成了,下面来看下我们定义的服务契约与回调契约吧。服务契约是客户端调用服务的接口。

服务契约:

   [ServiceContract(CallbackContract=typeof(ICoreCallbackService),
        SessionMode = SessionMode.Required,
        Namespace="GL")]
    public interface ICoreService
    {
        #region 连接操作
        /// <summary>
        /// 连接到服务器,创建Session
        /// </summary>
        /// <param name="clientSessionInfo">客户端连接信息</param>
        /// <returns>连接的SessionId</returns>
        [OperationContract]
        [FaultContract(typeof(GLFaultContract))]
        string Connect(ClientConnectionInfo clientSessionInfo);

        /// <summary>
        /// 重新连接到服务器
        /// </summary>
        /// <param name="sessionId">重新连接的SessionId</param>
        [OperationContract]
        [FaultContract(typeof(GLFaultContract))]
        void Reconnect(string sessionId);

        /// <summary>
        /// 断开与服务器
        /// </summary>
        [OperationContract(IsTerminating = true)]
        [FaultContract(typeof(GLFaultContract))]
        void Disconnect();
        #endregion

        #region 页面操作
        /// <summary>
        /// 创建PageService实例返回实例id
        /// </summary>
        /// <param name="serviceTypeName">serviceTypeName</param>
        /// <returns>instanceId</returns>
        [OperationContract(IsOneWay = false)]
        [FaultContract(typeof(GLFaultContract))]
        string CreatePageService(string serviceTypeName);

        /// <summary>
        /// 销毁PageService实例
        /// </summary>
        /// <param name="instanceId">instanceId</param>
        [OperationContract(IsOneWay = true)]
        void DestoryPageService(string instanceId);

        /// <summary>
        /// 调用一个页面服务方法
        /// </summary>
        /// <param name="request">页面调用请求</param>
        /// <returns>页面调用回复</returns>
        [OperationContract]
        [FaultContract(typeof(GLFaultContract))]
        Response CallPageService(Request request);

        /// <summary>
        /// 单向调用一个页面服务方法
        /// </summary>
        /// <param name="request">页面调用请求</param>
        [OperationContract(IsOneWay = true)]
        void OneWayCallPageService(Request request);

        /// <summary>
        /// 调用Session公共服务
        /// </summary>
        /// <param name="request">服务请求参数</param>
        /// <returns>服务请求回复</returns>
        [OperationContract]
        [FaultContract(typeof(GLFaultContract))]
        Response CallService(Request request);

        /// <summary>
        /// 单向调用Session公共服务
        /// </summary>
        /// <param name="request">服务请求参数</param>
        [OperationContract(IsOneWay = true)]
        void OneWayCallService(Request request);
        #endregion
View Code

回调契约:回调契约是服务调用客户端的接口。

    [ServiceContract]
    public interface ICoreCallbackService
    {
        /// <summary>
        /// 页面回调
        /// </summary>
        /// <param name="request">页面调用参数</param>
        /// <returns>页面调用回复</returns>
        [OperationContract]
        [FaultContract(typeof(GLFaultContract))]
        Response PageCallback(Request request);

        /// <summary>
        /// 单向页面回调
        /// </summary>
        /// <param name="request">页面调用参数</param>
        [OperationContract(IsOneWay = true)]
        void OneWayPageCallback(Request request);

        /// <summary>
        /// 客户端回调
        /// </summary>
        /// <param name="request">客户端回调参数</param>
        /// <returns>客户端回调回复</returns>
        [OperationContract]
        [FaultContract(typeof(GLFaultContract))]
        Response ClientCallback(Request request);

        /// <summary>
        /// 单向客户端回调
        /// </summary>
        /// <param name="request">客户端回调参数</param>
        [OperationContract(IsOneWay = true)]
        void OneWayClientCallback(Request request);
    }
View Code

 契约定义完成,接下来看下Session的定义

    public interface ISession: IPartAccess
    {
        string SessionID { get; }

        ClientConnectionInfo ClientConnectionInfo { get; }

        TimeSpan TimeOut { get; }

        ICoreCallbackService CallbackService { get; }

        IContextChannel ConnectionChannel { get; }

        IPageServiceManager PageServiceManager { get; }

        bool IsDisposed { get; }

        void ReConnect(IContextChannel ConnectionChannel, ICoreCallbackService Callback);

        #region PageService 操作

        string CreatePageService(string serviceTypeName);

        void DestoryPageService(string instanceId);

        Response CallPageService(Request request);

        void OneWayCallPageService(Request request);

        Response CallService(Request request);

        void OneWayCallService(Request request);

        #endregion
    }
public interface IPartAccess
    {

        /// <summary>
        /// 获取单实例插件
        /// </summary>
        /// <param name="partType"></param>
        /// <returns></returns>
        object GetSinglePart(Type partType);

        T GetSinglePart<T>();

        /// <summary>
        /// 获取多实例插件
        /// </summary>
        /// <param name="partType"></param>
        /// <returns></returns>
        IEnumerable<object> GetMultipleParts(Type partType);

        IEnumerable<T> GetMultipleParts<T>();

        /// <summary>
        /// 获取插件信息
        /// </summary>
        /// <param name="partType"></param>
        /// <returns></returns>
        IPartInformation GetPartInformation(object instance);

        /// <summary>
        /// 获取指定类型插件是否启用
        /// </summary>
        /// <param name="partType"></param>
        /// <returns></returns>
        bool IsPartEnabled(Type partType);
    }


   Session里定义了很多操作, 这里不仅有调用客户端的回调通道,还有操作Session级别的插件的访问,也有操作页面服务的访问接口,Session就是一个客户端与服务端连接与操作的桥梁,在这里可以进行任何想要的操作。

  接下来来看下我们核心服务和核心客户端的实现。

 核心服务:

   [ServiceBehavior(
    ConcurrencyMode = ConcurrencyMode.Multiple,
    InstanceContextMode = InstanceContextMode.PerSession,
    UseSynchronizationContext = false,
    AddressFilterMode = AddressFilterMode.Any)]
    public class CoreService : ICoreService
    {
        public ISession Session { get; private set; }

        public ISessionManager SessionManager { get; private set; }

        public CoreService() { }

        public string Connect(ClientConnectionInfo clientSessionInfo)
        {
            //新连接创建Session
            var connectionChannel = OperationContext.Current.Channel;
            var callbackChannel = OperationContext.Current.GetCallbackChannel<ICoreCallbackService>();
            Session = new Session(clientSessionInfo, connectionChannel, callbackChannel);
            SessionManager = PartPlatform.Instance.GetSinglePart<ISessionManager>();
            SessionManager.RegistSession(Session);
            return Session.SessionID;
        }

        public void Reconnect(string sessionId)
        {
            var connectionChannel = OperationContext.Current.Channel;
            var callbackChannel = OperationContext.Current.GetCallbackChannel<ICoreCallbackService>();
            Session.ReConnect(connectionChannel, callbackChannel);
        }

        public void Disconnect()
        {
            VerifySession();
            SessionManager.UnRegistSession(Session.SessionID);
        }

        public string CreatePageService(string serviceTypeName)
        {
            VerifySession();
            return Session.CreatePageService(serviceTypeName);
        }

        public void DestoryPageService(string instanceId)
        {
            VerifySession();
            Session.DestoryPageService(instanceId);
        }

        public Response CallPageService(Request request)
        {
            VerifySession();
            return Session.CallPageService(request);
        }

        public void OneWayCallPageService(Request request)
        {
            VerifySession();
            Session.OneWayCallPageService(request);
        }

        public Response CallService(Request request)
        {
            VerifySession();
            return Session.CallService(request);
        }

        public void OneWayCallService(Request request)
        {
            VerifySession();
            Session.OneWayCallService(request);
        }

        private void VerifySession()
        {
            if (Session == null)
            {
                throw new FaultException("Session 未创建");
            }

            if (Session.IsDisposed)
            {
                throw new FaultException("Session 已销毁");
            }
        }
    }
View Code

核心客户端:

  [SinglePart]
    [PalatformPart]
    [PartMetadataAttribute("PartName", "CoreClient")]
    [PartMetadataAttribute("PartType", typeof(CoreClient))]
    [PartMetadataAttribute("PartDependencies", new Type[] { typeof(IPageManager) })]
    [CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)]
    public class CoreClient : ICoreClient, ICoreServiceCallback
    {
        ILog _logger = LogManager.GetLogger(typeof(CoreClient));

        private IPageManager _pageManager = null;

        private CoreServiceClient _CoreServiceClient = null;

        public CoreClient()
        {

        }

        [PartActivationMethod]
        public void Initialize()
        {

        }

        private void InitalizeConnection()
        {
            if (_pageManager == null)
            {
                _pageManager = PartPlatform.Instance.GetSinglePart<IPageManager>();
            }
            _CoreServiceClient = new CoreServiceClient(new InstanceContext(this));
        }


        #region ICoreClient

        public string SessionId { get; private set; }

        public string Connect(ClientConnectionInfo sessionInfo)
        {
            InitalizeConnection();
            var sessionId = this.TryCallServer(() => { return _CoreServiceClient.Connect(sessionInfo); }, "Connect");

            _logger.InfoFormat("connect to server done, SessionId:{0}", sessionId);
            return sessionId;
        }

        private static object _lockObj = new object();

        public bool Reconnect()
        {
            lock (_lockObj)
            {
                if (this._CoreServiceClient.State == CommunicationState.Created)
                    return true;

                //_CoreServiceClient.Close;
                InitalizeConnection();
                this.TryCallServer(() => { _CoreServiceClient.Reconnect(this.SessionId); }, "Reconnect");

                return true;
            }
        }

        public void Disconnect()
        {
            VerifyConnection();
            this.TryCallServer(() => { _CoreServiceClient.Disconnect(); }, "Disconnect");
        }

        public string CreatePageService(string serviceTypeName)
        {
            VerifyConnection();
            return this.TryCallServer(() => { return _CoreServiceClient.CreatePageService(serviceTypeName); }, "CreatePageService", serviceTypeName);
        }

        public void DestoryPageService(string instanceId)
        {
            VerifyConnection();
            this.TryCallServer(() => { _CoreServiceClient.DestoryPageService(instanceId); }, "DestoryPageService", instanceId);
        }

        public Response CallPageService(Request request)
        {
            VerifyConnection();
            return this.TryCallServer(() => { return _CoreServiceClient.CallPageService(request); }, "CallPageService");
        }

        public void OneWayCallPageService(Request request)
        {
            VerifyConnection();
            this.TryCallServer(() => { _CoreServiceClient.OneWayCallPageService(request); }, "OneWayCallPageService");
        }

        public Response CallService(Request request)
        {
            VerifyConnection();
            return this.TryCallServer(() => { return _CoreServiceClient.CallService(request); }, "CallService");
        }

        public void OneWayCallService(Request request)
        {
            VerifyConnection();
            this.TryCallServer(() => { _CoreServiceClient.OneWayCallService(request); }, "OneWayCallService");
        }

        public void VerifyConnection()
        {

            if (_CoreServiceClient.State == CommunicationState.Faulted ||
                _CoreServiceClient.State == CommunicationState.Closing ||
                _CoreServiceClient.State == CommunicationState.Closed)
            {
                Reconnect();
            }
        }
        #region TryCallServer

        private void TryCallServer(Action task, string methodName, params string[] args)
        {
            DoTryCallServer(task, methodName, args);
        }

        private T TryCallServer<T>(Func<T> task, string methodName, params string[] args)
        {
            return (T)DoTryCallServer(task, methodName, args);
        }

        private object DoTryCallServer(Delegate method, string methodName, params string[] args)
        {
            try
            {
                _logger.DebugFormat("Start to Invoke {0},Args:{1}", methodName, string.Join(",", args));
                var result = method.DynamicInvoke();
                _logger.DebugFormat("Invoke {0} done.", methodName);
                return result;
            }
            catch (TargetInvocationException e)
            {
                //TODO:Callservice异常处理
                throw e;
            }
        }

        #endregion

        #endregion ICoreClient




        #region ICoreServiceCallback

        public Response PageCallback(Request request)
        {
            var page = _pageManager.GetPage(request.InstanceId);
            var invokeResult = page.ReflectCallInstanceMethod(request.MethodName, request.ParamTypes, request.Parameters.DeserializeToObject<object[]>());
            var response = new Response();
            response.InstanceId = request.InstanceId;
            response.Value = invokeResult.SerializeToByteArray();
            return response;
        }

        public void OneWayPageCallback(Request request)
        {
            var page = _pageManager.GetPage(request.InstanceId);
            page.ReflectCallInstanceMethod(request.MethodName, request.ParamTypes, request.Parameters.DeserializeToObject<object[]>());
        }

        public Response ClientCallback(Request request)
        {
            throw new NotImplementedException();
        }

        public void OneWayClientCallback(Request request)
        {
            throw new NotImplementedException();
        }

        #endregion ICoreServiceCallback
    }
View Code

这样我们的客户端服务端核心通信就实现了,下一篇将介绍页面与页面服务。

推荐wcf的参考文章:

 

DanielWise 的wcf系列文章

http://www.cnblogs.com/danielWise/archive/2011/06/23/2087937.html

MarkSun 的wcf文章

http://www.cnblogs.com/marksun/category/342642.html

这些文章看着都不错,我个人挺喜欢的。

posted @ 2013-06-16 14:40  广林  阅读(2038)  评论(0编辑  收藏  举报