zoukankan      html  css  js  c++  java
  • 一步步实现自己的框架系列(三):客户端服务端通信的实现

      离上次发表文章已经挺久的了,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

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

  • 相关阅读:
    基于 HTML5 + WebGL 实现的垃圾分类系统
    B/S 端基于 HTML5 + WebGL 的 VR 3D 机房数据中心可视化
    基于 Web 端 3D 地铁站可视化系统
    HTML5 + WebGL 实现的垃圾分类系统
    基于HTML5 WebGL的工业化3D电子围栏
    iOS 不支持 PWA,那又怎么样?
    PWA 入门: 写个非常简单的 PWA 页面
    iOS UTI
    canOpenURL: failed for URL: "weixin://app/wx 问题解决方式
    iOS扩大UIButton按钮的可点击区域
  • 原文地址:https://www.cnblogs.com/guanglin/p/3138100.html
Copyright © 2011-2022 走看看