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); } }
双向通讯类契约: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); } }
客户端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); } }
二、契约实现工程 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; } } }
双向通讯类实现: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 } }
三、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(); } } }
服务工厂类: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 } }
配置文件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>
至此,一个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 } }
服务代理工厂类: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 } }
自定义单通道真实代理: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; } } }
自定义双通道真实代理: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; } } }
单通道静态工厂类: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; } } }
双通道静态工厂类: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; } } }
服务终结点配置类: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"; } }
二、双通道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 } }
三、客户端演示程序。
后台代码:
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))); } } }
演示结果如下: