最近在写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);// }//}#endregionSystem.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;#endregionpublic 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;#endregionpublic 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;}#endregionpublic 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;#endregionpublic 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))); }} }
演示结果如下: