WCF 服务端+客户端动态调用

最近在写WCF服务相关代码,把项目中用到的通讯框架做了下整理,以备以后自己记忆。

WCF服务端:

包含契约定义:WCF.Contract、契约实现:WCF.Service 以及宿主主程序:WcfServerHost

本DEMO 为了为了演示,只定义了一个常用的计算算法和一个双向通讯的问号类

一、契约类工程 WCF.Contract

单向通讯契约:ICalculatorService

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;

namespace WCF.Contract
{
    /// <summary>
    /// 用户操作接口,提供算法运行
    /// </summary>
    [ServiceContract]
    public interface ICalculatorService
    {
        [OperationContract]
        double Add(double x, double y);

        [OperationContract]
        double Subtract(double x, double y);

        [OperationContract]
        double Multiply(double x, double y);

        [OperationContract]
        double Divide(double x, double y);
    }
}
View Code

双向通讯类契约:IDuplexMessageService

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;

namespace WCF.Contract
{
    [ServiceContract(CallbackContract = typeof(IDuplexMessageCallBack))]
    public interface IDuplexMessageService
    {
        /// <summary>
        /// 订阅服务,当回调产生时会对客户端进行推送
        /// </summary>
        [OperationContract(IsOneWay = true)]
        void SubscribeAlarm();

        /// <summary>
        /// 取消订阅,取消之后不推送
        /// </summary>
        [OperationContract(IsOneWay = true)]
        void UnSbuscribeAlarm();

        /// <summary>
        /// 客户端登录服务端
        /// </summary>
        /// <param name="jsonMessage">客户端登录服务端</param>
        [OperationContract(IsOneWay = true)]
        void Login(string username);
    }
}
View Code

客户端callback:IDuplexMessageCallBack

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;

namespace WCF.Contract
{
    /// <summary>
    /// 功    能:客户端回调契约
    /// 创 建 人: 
    /// 创建时间:
    /// </summary>
    [ServiceContract]
    public interface IDuplexMessageCallBack
    {
        /// <summary>
        /// 服务端返回给客户的问号信息
        /// </summary>
        /// <param name="jsonMessage">如果需要返回数据,使用json格式</param>
        [OperationContract]
        void Greet(string msg);

        /// <summary>
        /// 服务端返回给客户的离开信息
        /// </summary>
        /// <param name="jsonMessage">如果需要返回数据,使用json格式</param>
        [OperationContract]
        void Leave(string ip);
    }
}
View Code

二、契约实现工程 WCF.Service

单向通讯类工程实现类:CalculatorService

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WCF.Contract;

namespace WCF.Service
{
    /// <summary>
    /// 功    能:算法单向通道管理服务类型
    /// 创 建 人:龚安川
    /// 创建时间:2017-02-24
    /// </summary>
    public class CalculatorService : ICalculatorService
    {
      
        public double Add(double x, double y)
        {
            return x + y;
        }
        public double Subtract(double x, double y)
        {
            return x - y;
        }

        public double Multiply(double x, double y)
        {
            return x * y;
        }
        public double Divide(double x, double y)
        {
            return x / y;
        }
    }
}
View Code

双向通讯类实现:DuplexMessageService

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Text;
using System.Threading.Tasks;
using WCF.Contract;

namespace WCF.Service
{
    /// <summary>
    /// 功    能:DuplexMessageService双向通道管理服务类型-处理客户端事件回调
    /// 创 建 人:龚安川
    /// 创建时间:2017-02-24
    /// </summary>
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, UseSynchronizationContext = true, ConcurrencyMode = ConcurrencyMode.Multiple)]
    public class DuplexMessageService : IDuplexMessageService
    {
        #region [变量定义]

        /// <summary>
        /// 客户端回调通道字典
        /// key:IP
        /// value:回调通道
        /// </summary>
        private Dictionary<string, IDuplexMessageCallBack> Client_Callback_Dic = new Dictionary<string, IDuplexMessageCallBack>();

        /// <summary>
        /// 客户端回调字典锁
        /// </summary>
        private static object ClientLock = new object();

        #endregion

        //构造函数
        public DuplexMessageService()
        {

        }

        #region IDuplexMessageService 成员

        /// <summary>
        /// 订阅报警,当报警产生时会对客户端进行推送
        /// </summary>
        /// <returns>客户端唯一标识符</returns>
        public void SubscribeAlarm()
        {
            RemoteEndpointMessageProperty endpoint = OperationContext.Current.IncomingMessageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
            string ip = endpoint.Address;
            lock (ClientLock)
            {
                if (Client_Callback_Dic.ContainsKey(ip))
                {
                    if (!Client_Callback_Dic[ip].Equals(OperationContext.Current.GetCallbackChannel<IDuplexMessageCallBack>()))
                    {
                        /*如果通道和原来的不一样,客户端重启了*/
                        Client_Callback_Dic[ip] = OperationContext.Current.GetCallbackChannel<IDuplexMessageCallBack>();
                        try
                        {
                            //客户端报警推送
                            //Client_Callback_Dic[ip].Leave(ip);
                        }
                        catch { }
                    }
                }
                else
                {
                    Client_Callback_Dic.Add(ip, OperationContext.Current.GetCallbackChannel<IDuplexMessageCallBack>());
                    try
                    {
                        //客户端报警推送
                        //Client_Callback_Dic[ip].Leave(ip);
                    }
                    catch { }
                }
            }
        }

        /// <summary>
        /// 取消订阅,取消之后不推送
        /// </summary>
        /// <param name="clientID">客户端唯一标识符</param>
        public void UnSbuscribeAlarm()
        {
            RemoteEndpointMessageProperty endpoint = OperationContext.Current.IncomingMessageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
            string ip = endpoint.Address;

            lock (ClientLock)
            {
                if (Client_Callback_Dic.ContainsKey(ip))
                {
                    Client_Callback_Dic.Remove(ip);
                    return;
                }
            }
        }

        /// <summary>
        /// 客户端发往服务端加入信息
        /// </summary>
        /// <param name="clientID">客户端唯一标识符</param>
        /// <param name="jsonMessage">客户端发来的信息,如果需要传送信息使用json格式传送</param>
        public void Login(string username)
        {
            RemoteEndpointMessageProperty endpoint = OperationContext.Current.IncomingMessageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
            string ip = endpoint.Address;

            lock (ClientLock)
            {
                IDuplexMessageCallBack callBack = OperationContext.Current.GetCallbackChannel<IDuplexMessageCallBack>();

                if (Client_Callback_Dic.ContainsKey(ip))
                {
                    if (!Client_Callback_Dic[ip].Equals(callBack))
                    {
                        Client_Callback_Dic[ip] = OperationContext.Current.GetCallbackChannel<IDuplexMessageCallBack>();
                    }
                }
                else
                {
                    Client_Callback_Dic.Add(ip, callBack);
                }

                try
                {
                    //服务端调用客户端问号信息
                    Client_Callback_Dic[ip].Greet("欢迎您:" + ip);
                }
                catch { }
            }
        }

        #endregion
    }
}
View Code

三、WCF宿主程序 WcfServerHost 直接建立的控制台程序

服务管理类:ServerManagement

using System;
using System.Collections.Generic;
using System.Linq;
using System.Management;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace WcfServerHost
{
    /// <summary>
    /// 功    能:WCF服务控制类
    /// 创 建 人:龚安川
    /// 创建时间:2017-02-24
    /// </summary>
    public class ServerManagement
    {
        /// <summary>
        /// 服务开启
        /// </summary>
        public static void Start()
        {
            new Thread(() =>
            {
                log4net.ILog log = log4net.LogManager.GetLogger(typeof(ServerManagement));
                try
                {
                    ConnectionOptions coOptions = new ConnectionOptions();
                    coOptions.Impersonation = ImpersonationLevel.Impersonate;
                    // CIMV2 is a namespace that contains all of the core OS and hardware classes.
                    // CIM (Common Information Model) which is an industry standard for describing
                    // data about applications and devices so that administrators and software
                    // management programs can control applications and devices on different
                    // platforms in the same way, ensuring interoperability across a network.
                    ManagementScope mgmtScope = new ManagementScope(@"root\CIMV2", coOptions);
                    mgmtScope.Connect();
                    ManagementObject wmiService;
                    wmiService = new ManagementObject("Win32_Service.Name='" + "Pisservice" + "'");
                    ManagementBaseObject InParam = wmiService.GetMethodParameters("Change");
                    InParam["DesktopInteract"] = true;
                    wmiService.InvokeMethod("Change", InParam, null);
                }
                catch (Exception ex)
                {
                    log.Error(ex);
                }

                #region [验证启动关联服务]

                ////获取系统服务
                //ServiceController[] services = ServiceController.GetServices();
                //ServiceController mysqlService = null;
                //foreach (ServiceController sc in services)
                //{
                //    if (sc.ServiceName.ToLower().Equals("mysql") || sc.ServiceName.ToLower().Equals("mariadb"))
                //    {
                //        mysqlService = sc;
                //        break;
                //    }
                //}

                //if (mysqlService == null)
                //    throw new Exception("没有找到mysql服务");

                //log.Debug(mysqlService.Status.ToString());
                //if (mysqlService.Status == ServiceControllerStatus.Stopped)
                //{
                //    for (int i = 1; i < 1000; i++)
                //    {
                //        log.Debug(i);
                //        if (mysqlService.Status != ServiceControllerStatus.Stopped)
                //        {
                //            break;
                //        }
                //        if (i % 10 == 0)
                //        {
                //            try
                //            {
                //                log.Debug(i + "start");
                //                mysqlService.Start();
                //            }
                //            catch (Exception ex) { log.Debug(ex); }
                //        }
                //        System.Threading.Thread.Sleep(1000);
                //    }
                //}

                #endregion



                System.Threading.Thread.Sleep(20000);   //给数据库加载时间

                //初始化缓存服务
                //new ServiceCacheManagement().InitCache();

                //创建本地服务
                ServiceHostFactory.Instance.StartAllService();

                ////开启级联监听服务
                //AnalysisCascade.GetInstance().StartAnalysis();

                ////开启广播监听服务
                //AnalysisBroadCast.GetInstance().StartAnalysis();

                //BroadCast.GetInstance().StartListen();
                ////BroadCastTranspond.GetInstance().Start();
                ////BroadCastCascade.GetInstance().Start();

                ////CorrectTime.GetInstance().Start();

                //Battery.GetInstance().CheckBattery();
                //DetectComputerDrive.GetInstance().DetectDrive();
                //开启日志清理服务
                //ClearLogManagement.StartClear();
            }) { IsBackground = true }.Start();
        }

        /// <summary>
        /// 服务停止
        /// </summary>
        public static void Stop()
        {
            //AnalysisBroadCast.GetInstance().StopAnalysis();
            //ClearLogManagement.StopClear();
            //CorrectTime.GetInstance().Stop();
            //BroadCast.GetInstance().StopListen();

            //AnalysisCascade.GetInstance().StopAnalysis();
            //BroadCastTranspond.GetInstance().Stop();
            //BroadCastCascade.GetInstance().Stop();

            ServiceHostFactory.Instance.StopAllService();
        }
    }
}
View Code

服务工厂类:ServiceHostFactory,

using log4net;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Reflection;
using System.ServiceModel;
using System.ServiceModel.Configuration;
using System.Text;
using System.Threading.Tasks;
using WCF.Service;

namespace WcfServerHost
{
    /// <summary>
    /// 功    能:WCF服务工厂类
    /// 创 建 人:龚安川
    /// 创建时间:2017-02-24
    /// </summary>
    public class ServiceHostFactory
    {

        #region [变量定义]

        /// <summary>
        /// 日志管理对象
        /// </summary>
        private ILog log = LogManager.GetLogger(typeof(ServiceHostFactory));

        /// <summary>
        /// 此处必须用单例,防止多次开启导致异常
        /// </summary>
        private static ServiceHostFactory instance = null;

        /// <summary>
        /// 对象锁
        /// </summary>
        private static object padlock = new object();

        /// <summary>
        /// 服务列表
        /// </summary>
        private Dictionary<Type, ServiceHost> m_HostDic;

        /// <summary>
        /// 服务类型集合
        /// </summary>
        private List<Type> Services = new List<Type>();

        /// <summary>
        /// 标识服务是否开启
        /// </summary>
        private bool isOpen = false;

        #endregion

        public static ServiceHostFactory Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (padlock)
                    {
                        if (instance == null) instance = new ServiceHostFactory();
                    }
                }
                return instance;
            }
        }

        private ServiceHostFactory()
        {
            m_HostDic = new Dictionary<Type, ServiceHost>();

            //初始化服务
            InitServices();

            GetCfg();
        }

        /// <summary>
        /// 析构函数
        /// </summary>
        ~ServiceHostFactory()
        {
            StopAllService();
        }


        #region [公开方法]

        /// <summary>
        /// 开启所有服务
        /// </summary>
        public void StartAllService()
        {
            lock (padlock)
            {
                try
                {
                    if (isOpen)
                        return;

                    if (m_HostDic == null)
                        return;

                    var keys = m_HostDic.Keys.ToList();
                    foreach (var serviceType in keys)
                    {
                        var host = m_HostDic[serviceType];

                        if (host == null)
                            m_HostDic[serviceType] = CreatHost(serviceType);

                        var state = m_HostDic[serviceType].State;

                        if (state == CommunicationState.Faulted)
                        {
                            m_HostDic[serviceType].Abort();
                            m_HostDic[serviceType] = CreatHost(serviceType);
                        }

                        if (state == CommunicationState.Closed || state == CommunicationState.Closing)
                            m_HostDic[serviceType] = CreatHost(serviceType);

                        if (!(state == CommunicationState.Opened || state == CommunicationState.Opening))
                            m_HostDic[serviceType].Open();
                    }//end foreach

                    isOpen = true;

                    //LogManagent.Log("开启服务", Util.PISLOG_TYPE_RUNING, "服务启动成功");
                }
                catch (Exception ex)
                {
                    //LogManagent.Log("开启服务", Util.PISLOG_TYPE_RUNING, "服务启动失败");
                    log.Error(ex);
                    StopAllService();
                    throw ex;
                }
            }
        }

        /// <summary>
        /// 关闭所有服务
        /// </summary>
        public void StopAllService()
        {
            if (m_HostDic == null)
                return;

            var keys = m_HostDic.Keys.ToList();
            foreach (var serviceType in keys)
            {
                var host = m_HostDic[serviceType];

                if (host == null)
                    continue;
                try
                {
                    host.Close(TimeSpan.FromSeconds(5));
                }
                catch
                {
                    host.Abort();
                }
            }

            //LogManagent.Log("关闭服务", Util.PISLOG_TYPE_RUNING, "服务关闭成功");
            isOpen = false;
        }

        #endregion

        #region [私有方法]

        //初始计划服务
        private void InitServices()
        {
            Services.Clear();
            Services.Add(typeof(CalculatorService));
            Services.Add(typeof(DuplexMessageService));
        }

        // 从配置文件中读取需要开启的服务列表
        private void GetCfg()
        {
            //循环所有服务类型
            foreach (Type t in Services)
            {
                ServiceHost host = CreatHost(t);
                m_HostDic[t] = host;
            }
        }

        //创建当前类型的服务
        private ServiceHost CreatHost(Type svcType)
        {
            ServiceHost host = new ServiceHost(svcType);
            //调试时打开IncludeExceptionDetailInFaults,可将异常传递到客户端;
            ServiceBehaviorAttribute behavior = host.Description.Behaviors.Find<ServiceBehaviorAttribute>();
            behavior.IncludeExceptionDetailInFaults = true;
            return host;
        }

        #endregion
    }
}
View Code

配置文件App.config,利用system.serviceModel 节点,动态配置需要管理的服务

  <!--WCF配置-->
  <system.serviceModel>
    <!--bindings-->
    <bindings>
      <netTcpBinding>
        <binding name="PisBinding" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647">
          <security mode="None" />
        </binding>
        <binding name="FileServiceBinding" sendTimeout="00:10:00" transferMode="Streamed" maxReceivedMessageSize="2147483647">
          <readerQuotas maxDepth="64" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
          <security mode="None" />
        </binding>
      </netTcpBinding>
    </bindings>
    <!--services-->
    <services>
      <service behaviorConfiguration="PisServiceBehavior" name="WCF.Service.CalculatorService">
        <endpoint address="" binding="netTcpBinding" bindingConfiguration="PisBinding" name="CalculatorService" contract="WCF.Contract.ICalculatorService" />
        <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="net.tcp://192.168.1.85:8888/CalculatorService" />
          </baseAddresses>
        </host>
      </service>
      <service behaviorConfiguration="PisServiceBehavior" name="WCF.Service.DuplexMessageService">
        <endpoint address="" binding="netTcpBinding" bindingConfiguration="PisBinding" name="DuplexMessageService" contract="WCF.Contract.IDuplexMessageService" />
        <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="net.tcp://192.168.1.85:8888/DuplexMessageService" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <!--behaviors-->
    <behaviors>
      <serviceBehaviors>
        <behavior name="PisServiceBehavior">
          <serviceMetadata httpGetEnabled="false" httpsGetEnabled="false" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
View Code

至此,一个WCF服务端就实现好了

WCF客户端

包含WCF客户端实现类:WCF.Client、双向通讯CallBackHandle:WCF.Duplex 以及演示程序:WcfClient

 

一、客户端代理工厂: WCF.Client

 WCF客户端类:WCFClient

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WCF.Contract;

namespace WCF.Client
{
    /// <summary>
    /// 功    能:WCFClient 对象
    /// 创 建 人:龚安川
    /// 创建时间:2017-02-24
    /// </summary>
    public class WCFClient
    {
        #region [变量定义]

        #region [Static]

        /// <summary>
        /// WCF客户端对象-单例模式
        /// </summary>
        static WCFClient instance = null;

        static object padlock = new object();

        /// <summary>
        /// 实例化客户端代理-单例模式;
        /// </summary>
        public static WCFClient Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (padlock)
                    {
                        if (instance == null)
                        {
                            instance = new WCFClient();
                        }
                    }
                }
                return instance;
            }
        }
        #endregion

        /// <summary>
        /// 服务终结点集合对象
        /// </summary>
        private Dictionary<string, string> _EndpointNameDic;

        #endregion       

        private WCFClient()
        {
            InitEndpointNameDic();
        }

        #region [公开方法]

        /// <summary>
        /// 获取WCF客户端代理-单向通讯
        /// </summary>
        /// <typeparam name="T">契约类型</typeparam>
        /// <returns></returns>
        public T GetService<T>()
        {
            return ServiceProxyFactory.Create<T>(_EndpointNameDic[typeof(T).Name]);
        }

        /// <summary>
        /// 获取WCF客户端代理-双向通讯
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="context"></param>
        /// <returns></returns>
        public T GetService<T>(object context)
        {
            return ServiceProxyFactory.Create<T>(context, _EndpointNameDic[typeof(T).Name]);
        }

        #endregion

        #region [私有方法]

        //初始化终结点集合
        private void InitEndpointNameDic()
        {
            _EndpointNameDic = new Dictionary<string, string>();

            _EndpointNameDic.Add(typeof(ICalculatorService).Name, EndpointNames.CalculatorService);
            _EndpointNameDic.Add(typeof(IDuplexMessageService).Name, EndpointNames.DuplexMessageService);          
        }

        #endregion   
    }
}
View Code

服务代理工厂类:ServiceProxyFactory

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WCF.Client
{   
    /// <summary>
    /// 功    能:服务代理工厂
    /// 创 建 人:龚安川
    /// 创建时间:XXXX
    /// </summary>
    public static class ServiceProxyFactory
    {
        #region [公开方法]

        /// <summary>
        /// 创建到指定终结点地址的指定类型的通道;
        /// </summary>
        /// <typeparam name="T">由通道工厂生成的通道类型</typeparam>
        /// <param name="endpointName">用于终结点的配置名称</param>
        /// <returns></returns>
        public static T Create<T>(string endpointName)
        {
            if (string.IsNullOrEmpty(endpointName))
            {
                throw new ArgumentNullException("endpointName");
            }

            //通过自定义的RealProxy创建TransparentProxy供客户端代码调用,统一管理调用异常记录
            return (T)(new ServiceRealProxy<T>(endpointName).GetTransparentProxy());
        }

        /// <summary>
        /// 创建到指定终结点地址的指定类型的通道;
        /// 在服务和客户端上的回调实例之间创建双工通道;
        /// </summary>
        /// <typeparam name="T">由通道工厂生成的通道类型</typeparam>
        /// <param name="callbackObject">客户端用以侦听来自所连接服务的消息</param>
        /// <param name="endpointName">用于终结点的配置名称</param>
        /// <returns></returns>
        public static T Create<T>(object callbackObject, string endpointName)
        {
            if (callbackObject == null)
            {
                throw new ArgumentNullException("callbackObject");
            }
            if (string.IsNullOrEmpty(endpointName))
            {
                throw new ArgumentNullException("endpointName");
            }

            //通过自定义的RealProxy创建TransparentProxy供客户端代码调用,统一管理调用异常记录
            return (T)new DuplexServiceRealProxy<T>(callbackObject, endpointName).GetTransparentProxy();
        }

        /// <summary>
        /// 创建到指定终结点地址的指定类型的通道;
        /// 在服务和客户端上的回调实例之间创建双工通道;
        /// 调用结束后根据指定决定是否立即释放通道;
        /// </summary>
        /// <typeparam name="T">由通道工厂生成的通道类型</typeparam>
        /// <param name="callbackObject">客户端用以侦听来自所连接服务的消息</param>
        /// <param name="endpointName">用于终结点的配置名称</param>
        /// <param name="immediateRelease">是否立即释放</param>
        /// <returns></returns>
        public static T Create<T>(object callbackObject, string endpointName, bool immediateRelease)
        {
            if (callbackObject == null)
            {
                throw new ArgumentNullException("callbackObject");
            }
            if (string.IsNullOrEmpty(endpointName))
            {
                throw new ArgumentNullException("endpointName");
            }
            return (T)new DuplexServiceRealProxy<T>(callbackObject, endpointName, immediateRelease).GetTransparentProxy();
        }

        #endregion       
    }
}
View Code

自定义单通道真实代理:ServiceRealProxy

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;

namespace WCF.Client
{
    /// <summary>
    /// 功    能:自定义单通道真实代理
    /// 创 建 人:龚安川
    /// 创建时间:XXXX
    /// 说明:RealProxy中加入异常捕获、记录日志等非业务逻辑代码
    ///      这些代码在每次调用服务端方法时都会被调用
    /// </summary>
    internal class ServiceRealProxy<T> : RealProxy
    {
        #region [变量定义]

        /// <summary>
        /// 终结点名称
        /// </summary>
        private string _endpointName;

        #endregion      

        public ServiceRealProxy(string endpointName)
            : base(typeof(T))
        {
            if (string.IsNullOrEmpty(endpointName))
            {
                throw new ArgumentNullException("endpointName");
            }
            this._endpointName = endpointName;
        }
        
        public override IMessage Invoke(IMessage msg)
        {
            T channel = ChannelFactoryCreator.Create<T>(this._endpointName).CreateChannel();
            IMethodCallMessage methodCall = (IMethodCallMessage)msg;
            IMethodReturnMessage methodReturn = null;
            object[] copiedArgs = Array.CreateInstance(typeof(object), methodCall.Args.Length) as object[];
            methodCall.Args.CopyTo(copiedArgs, 0);
            try
            {
                object returnValue = methodCall.MethodBase.Invoke(channel, copiedArgs);
                methodReturn = new ReturnMessage(returnValue, copiedArgs, copiedArgs.Length, methodCall.LogicalCallContext, methodCall);
                (channel as ICommunicationObject).Close();
            }
            catch (Exception ex)
            {
                if (ex.InnerException is CommunicationException || ex.InnerException is TimeoutException)
                {
                    (channel as ICommunicationObject).Abort();
                }

                //记录异常信息;
                StringBuilder message = new StringBuilder();
                message.AppendLine(string.Format("An exception is throw when invoking {0}.{1} method.",
                    channel.GetType().FullName, methodCall.MethodName));

                if (ex.InnerException != null)
                {
                    methodReturn = new ReturnMessage(ex.InnerException, methodCall);

                    message.AppendLine(string.Format("Exception Type:{0}",
                        ex.InnerException.GetType().AssemblyQualifiedName));
                    message.AppendLine(string.Format("Stack Trace:{0}",
                        ex.InnerException.StackTrace));
                }
                else
                {
                    methodReturn = new ReturnMessage(ex, methodCall);

                    message.AppendLine(string.Format("Exception Type:{0}",
                        ex.InnerException.GetType().AssemblyQualifiedName));
                    message.AppendLine(string.Format("Stack Trace:{0}",
                        ex.InnerException.StackTrace));
                }

                message.AppendLine("Input Arguments:");
                for (int i = 0; i < methodCall.InArgs.Length; i++)
                {
                    message.AppendLine(string.Format("{0}={1}", methodCall.GetInArgName(i), methodCall.GetInArg(i)));
                }

                Debug.WriteLine(message);
            }

            return methodReturn;
        }
    }
}
View Code

自定义双通道真实代理:DuplexServiceRealProxy

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;

namespace WCF.Client
{
    /// <summary>
    /// 功    能:自定义双工通道真实代理; 
    /// 创 建 人:龚安川
    /// 创建时间:
    /// </summary>
    /// <typeparam name="T"></typeparam>
    internal class DuplexServiceRealProxy<T> : RealProxy
    {
        #region [变量定义]

        /// <summary>
        /// 通道集合
        /// </summary>
        private static Hashtable channels = new Hashtable();

        /// <summary>
        /// 终结点名称
        /// </summary>
        private string _endpointName;

        /// <summary>
        /// 回调对象
        /// </summary>
        private object _callbackObject;

        /// <summary>
        /// 通道是否已释放
        /// </summary>
        private bool _immediateRelease;

        #endregion

        public DuplexServiceRealProxy(object callbackObject, string endpointName, bool immediateRelease = false)
            : base(typeof(T))
        {
            if (callbackObject == null)
            {
                throw new ArgumentNullException("callbackObject");
            }
            if (string.IsNullOrEmpty(endpointName))
            {
                throw new ArgumentNullException("endpointName");
            }
            this._callbackObject = callbackObject;
            this._endpointName = endpointName;
            this._immediateRelease = immediateRelease;
        }

        #region [私有方法]

        private T GetChannel(string channelKey)
        {
            T channel = default(T);
            if (this._immediateRelease)
            {
                //重新创建代理通道
                channel = DuplexChannelFactoryCreator.Create<T>(this._callbackObject, this._endpointName).CreateChannel();
            }
            else
            {
                if (channels.ContainsKey(channelKey))
                {
                    //赋值当前通讯通道
                    channel = (T)channels[channelKey];
                }

                //检测通道是否关闭或出错;
                if (channel != null)
                {
                    var state = (channel as ICommunicationObject).State;
                    if (state == CommunicationState.Closed || state == CommunicationState.Faulted)//通道已关闭或出错;
                    {
                        if (state == CommunicationState.Faulted) (channel as ICommunicationObject).Abort();//通道出错,强制关闭;
                        channel = default(T);
                    }
                }
                if (channel == null)
                {
                    channel = DuplexChannelFactoryCreator.Create<T>(this._callbackObject, this._endpointName).CreateChannel();
                    lock (channels.SyncRoot)
                    {
                        channels[channelKey] = channel;
                    }
                }
            }
            return channel;
        }

        #endregion

        public override IMessage Invoke(IMessage msg)
        {
            string channelKey = this._endpointName;

            //获取当前通道
            T channel = GetChannel(channelKey);

            IMethodCallMessage methodCall = (IMethodCallMessage)msg;
            IMethodReturnMessage methodReturn = null;
            object[] copiedArgs = Array.CreateInstance(typeof(object), methodCall.Args.Length) as object[];
            methodCall.Args.CopyTo(copiedArgs, 0);
            try
            {
                object returnValue = methodCall.MethodBase.Invoke(channel, copiedArgs);
                methodReturn = new ReturnMessage(returnValue, copiedArgs, copiedArgs.Length, methodCall.LogicalCallContext, methodCall);
                if (this._immediateRelease) (channel as ICommunicationObject).Close();
            }
            catch (Exception ex)
            {
                if (ex.InnerException is FaultException)
                {

                }
                else if (ex.InnerException is CommunicationException || ex.InnerException is TimeoutException)
                {
                    (channel as ICommunicationObject).Abort();
                    lock (channels.SyncRoot)
                    {
                        channels[channelKey] = default(T);
                    }
                }

                //记录异常信息;
                StringBuilder message = new StringBuilder();
                message.AppendLine(string.Format("An exception is throw when invoking {0}.{1} method.",
                    channel.GetType().FullName, methodCall.MethodName));

                if (ex.InnerException != null)
                {
                    methodReturn = new ReturnMessage(ex.InnerException, methodCall);

                    message.AppendLine(string.Format("Exception Type:{0}",
                        ex.InnerException.GetType().AssemblyQualifiedName));
                    message.AppendLine(string.Format("Stack Trace:{0}",
                        ex.InnerException.StackTrace));
                }
                else
                {
                    methodReturn = new ReturnMessage(ex, methodCall);

                    message.AppendLine(string.Format("Exception Type:{0}",
                        ex.InnerException.GetType().AssemblyQualifiedName));
                    message.AppendLine(string.Format("Stack Trace:{0}",
                        ex.InnerException.StackTrace));
                }

                message.AppendLine("Input Arguments:");
                for (int i = 0; i < methodCall.InArgs.Length; i++)
                {
                    message.AppendLine(string.Format("{0}={1}", methodCall.GetInArgName(i), methodCall.GetInArg(i)));
                }

                Debug.WriteLine(message);
            }

            return methodReturn;
        }

    }
}
View Code

单通道静态工厂类:ChannelFactoryCreator

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;

namespace WCF.Client
{
    /// <summary>
    /// 功    能:ChannelFactory单通道静态工厂类
    /// 创 建 人:龚安川
    /// 创建时间:XXXX
    /// </summary>
    internal static class ChannelFactoryCreator
    {
        /// <summary>
        /// 通道工厂集合对象
        /// </summary>
        private static Hashtable channelFactories = new Hashtable();

        /// <summary>
        /// 通道创建
        /// </summary>
        /// <typeparam name="T">契约类型</typeparam>
        /// <param name="endpointName">终结点名称</param>
        /// <returns></returns>
        public static ChannelFactory<T> Create<T>(string endpointName)
        {
            if (string.IsNullOrEmpty(endpointName))
            {
                throw new ArgumentNullException("endpointName");
            }

            ChannelFactory<T> channelFactory = null;

            //判断当前终结点是否存在
            if (channelFactories.ContainsKey(endpointName))
            {
                //返回当前通道工厂
                channelFactory = channelFactories[endpointName] as ChannelFactory<T>;
            }

            if (channelFactory == null)
            {
                //创建新的通道工厂
                channelFactory = new ChannelFactory<T>(endpointName);

                //锁定通道工厂集合
                lock (channelFactories.SyncRoot)
                {
                    //更新当前通道终结点对象
                    channelFactories[endpointName] = channelFactory;
                }
            }

            return channelFactory;
        }
    }
}
View Code

双通道静态工厂类:DuplexChannelFactoryCreator

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;

namespace WCF.Client
{
    /// 功    能:DuplexChannelFactoryCreator双通道静态工厂类
    /// 创 建 人:龚安川
    /// 创建时间:XXXX
    /// </summary>
    internal static class DuplexChannelFactoryCreator
    {
        /// <summary>
        /// 通道工厂集合对象
        /// </summary>
        private static Hashtable channelFactories = new Hashtable();

        public static DuplexChannelFactory<T> Create<T>(object callbackObject, string endpointName)
        {
            if (callbackObject == null)
            {
                throw new ArgumentNullException("callbackObject");
            }
            if (string.IsNullOrEmpty(endpointName))
            {
                throw new ArgumentNullException("endpointName");
            }

            DuplexChannelFactory<T> channelFactory = null;

            //判断当前终结点是否存在
            if (channelFactories.ContainsKey(endpointName))
            {
                //返回当前通道工厂
                channelFactory = channelFactories[endpointName] as DuplexChannelFactory<T>;
            }

            if (channelFactory == null)
            {
                channelFactory = new DuplexChannelFactory<T>(callbackObject, endpointName);
                //锁定通道工厂集合
                lock (channelFactories.SyncRoot)
                {
                    //更新当前通道终结点对象
                    channelFactories[endpointName] = channelFactory;
                }
            }
            return channelFactory;

        }
    }
}
View Code

服务终结点配置类:EndpointNames

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WCF.Client
{   
    /// <summary>
    /// 功    能:终结点名字
    /// 创 建 人:龚安川
    /// 创建时间:XXXX
    /// </summary>
    public static class EndpointNames
    {
        /// <summary>
        /// 计算服务
        /// </summary>
        public const string CalculatorService = "CalculatorService";  
  
        /// <summary>
        /// 双向通讯服务
        /// </summary>
        public const string DuplexMessageService = "DuplexMessageService";        
    }
}
View Code

 

二、双通道CallBackHandle: WCF.Duplex

DuplexMessage客户端回调服务实现类: DuplexMessageCallBack

利用callback方法,将回掉通过注册的事件传递到外面

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using WCF.Client;
using WCF.Contract;

namespace WCF.Duplex
{
    /// <summary>
    /// 功    能:客户端回调服务实现类
    /// 创 建 人:龚安川
    /// 创建时间:
    /// </summary>
    public class DuplexMessageCallBack : IDuplexMessageCallBack
    {
        #region [变量定义]

        /// <summary>
        /// 唯一实例
        /// </summary>
        private static DuplexMessageCallBack _Instance;

        /// <summary>
        /// 客户端回调对象锁
        /// </summary>
        private static object lockObj = new object();

        /// <summary>
        /// 心跳线程
        /// </summary>
        private Thread HeadtThread = null;

        /// <summary>
        /// 订阅线程
        /// </summary>
        private Thread SubscribeThread = null;

        /// <summary>
        /// 是否心跳检测
        /// </summary>
        private bool isHearting = false;

        /// <summary>
        /// 是否订阅
        /// </summary>
        private bool isSubscribe = false;

        #endregion

        public DuplexMessageCallBack()
        {

        }

        /// <summary>
        /// 获取唯一实例
        /// </summary>
        /// <returns></returns>
        public static DuplexMessageCallBack GetInstance()
        {
            lock (lockObj)
            {
                if (_Instance == null)
                    _Instance = new DuplexMessageCallBack();
                return _Instance;
            }
        }

        #region [委托和事件定义]

        /// <summary>
        /// 收到问号委托
        /// </summary>
        /// <param name="messages"></param>
        public delegate void DelegateGreet(string messages);

        /// <summary>
        /// 收到离开委托
        /// </summary>
        /// <param name="ip"></param>
        public delegate void DelegateLeave(string ip);

        /// <summary>
        /// 收到报警事件
        /// </summary>
        public event DelegateGreet OnGreet;

        /// <summary>
        /// 恢复报警事件
        /// </summary>
        public event DelegateLeave OnLeave;


        #endregion

        #region IDuplexMessageCallBack 成员

        //服务端问好
        public void Greet(string msg)
        {
            if (null != OnGreet)
            {
                OnGreet(msg);
            }
        }

        //服务端推送客户端离开
        public void Leave(string ip)
        {
            if (null != OnLeave)
            {
                OnLeave(ip);
            }
        }

        #endregion

        #region [公开方法]

        /// <summary>
        /// 订阅报警
        /// </summary>
        public void SubScribeAlarm()
        {
            /*如果第一次订阅失败,一直订阅直到成功为止*/
            if (!isSubscribe)
            {
                ThreadStart sbuTs = new ThreadStart(Subscribing);
                SubscribeThread = new Thread(sbuTs);
                SubscribeThread.IsBackground = true;
                SubscribeThread.Start();
            }

            if (!isHearting)
            {
                isHearting = true;
                ThreadStart ts = new ThreadStart(Hearting);
                HeadtThread = new Thread(ts);
                HeadtThread.IsBackground = true;
                HeadtThread.Start();
            }
        }

        /// <summary>
        /// 取消报警订阅
        /// </summary>
        public void UnSunScribeAlarm()
        {
            isHearting = false;
            if (HeadtThread != null)
            {
                HeadtThread.Abort();
            }

            if (!isSubscribe)
            {
                if (SubscribeThread != null)
                {
                    SubscribeThread.Abort();
                }
            }
            else
            {
                try
                {
                    InstanceContext context = new InstanceContext(this);
                    WCFClient.Instance.GetService<IDuplexMessageService>(context).UnSbuscribeAlarm();
                }
                catch { }

                isSubscribe = false;
            }
        }

        #endregion

        #region [私有方法]

        //订阅定时器事件
        private void Subscribing()
        {
            InstanceContext context = new InstanceContext(this);
            while (!isSubscribe)
            {
                try
                {
                    //客户端请求报警
                    WCFClient.Instance.GetService<IDuplexMessageService>(context).SubscribeAlarm();
                    isSubscribe = true;
                }
                catch (ThreadAbortException ex)
                {
                    return;
                }
                catch
                {
                    Thread.Sleep(5000);
                }
            }
        }

        //WCF客户端心跳线程
        private void Hearting()
        {
            InstanceContext context = new InstanceContext(this);

            while (isHearting)
            {
                if (!isSubscribe)
                {
                    Thread.Sleep(5000);
                    continue;
                }

                try
                {
                    //客户端发送线条-避免客户端下线
                    Thread.Sleep(5000);
                    WCFClient.Instance.GetService<IDuplexMessageService>(context).Login(Guid.NewGuid().ToString());
                }
                catch (ThreadAbortException ex)
                {
                    return;
                }
                catch (Exception ex)
                {

                }
            }
        }

        #endregion
    }
}
View Code

三、客户端演示程序。

 

 后台代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using WCF.Client;
using WCF.Contract;
using WCF.Duplex;

namespace WcfClient
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            //不捕获线程错误调用
            //CheckForIllegalCrossThreadCalls = false;
        }

        private void btnOne_Click(object sender, EventArgs e)
        {
            //仅用一个加法测试
            var result = WCFClient.Instance.GetService<ICalculatorService>().Add(2, 3);
            listBox1.Items.Add("单向加运算结果为:" + result);

        }

        private void btnTwo_Click(object sender, EventArgs e)
        {
            //创建客户端回调服务对象
            DuplexMessageCallBack callback = new DuplexMessageCallBack();

            //创建通讯上下文
            InstanceContext context = new InstanceContext(callback);

            //收到问号信号事件注册
            callback.OnGreet += Form1_OnGreet;

            //收到离开信号事件注册
            callback.OnLeave += Form1_OnLeave;

            //订阅报警
            //DuplexMessageCallBack.GetInstance().SubScribeAlarm();

            try
            {
                WCFClient.Instance.GetService<IDuplexMessageService>(context).Login("张三");
            }
            catch (Exception ex)
            {
                MessageBox.Show("客户端调用双向服务登录异常!");
            }
            //listBox1.Items.Add("单向加运算结果为:" + result);
        }

        //问号处理
        void Form1_OnLeave(string ip)
        {
            listBox1.Items.Add("双向收到离开信号:" + ip);
        }

        //离开处理
        void Form1_OnGreet(string messages)
        {
            listBox1.Items.Add("双向收到问号信号:" + messages);

            //建议安全的访问方式
            listBox1.Invoke(new Action(() =>   listBox1.Items.Add("双向收到问号信号:" + messages))); 
        }
    }
}
View Code

演示结果如下:

 

转载于:https://www.cnblogs.com/82767136/articles/6439217.html

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值