最近在写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{ ////// 用户操作接口,提供算法运行 /// [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 { ////// 订阅服务,当回调产生时会对客户端进行推送 /// [OperationContract(IsOneWay = true)] void SubscribeAlarm(); ////// 取消订阅,取消之后不推送 /// [OperationContract(IsOneWay = true)] void UnSbuscribeAlarm(); ////// 客户端登录服务端 /// /// 客户端登录服务端 [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{ ////// 功 能:客户端回调契约 /// 创 建 人: /// 创建时间: /// [ServiceContract] public interface IDuplexMessageCallBack { ////// 服务端返回给客户的问号信息 /// /// 如果需要返回数据,使用json格式 [OperationContract] void Greet(string msg); ////// 服务端返回给客户的离开信息 /// /// 如果需要返回数据,使用json格式 [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{ ////// 功 能:算法单向通道管理服务类型 /// 创 建 人:龚安川 /// 创建时间:2017-02-24 /// 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{ ////// 功 能:DuplexMessageService双向通道管理服务类型-处理客户端事件回调 /// 创 建 人:龚安川 /// 创建时间:2017-02-24 /// [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, UseSynchronizationContext = true, ConcurrencyMode = ConcurrencyMode.Multiple)] public class DuplexMessageService : IDuplexMessageService { #region [变量定义] ////// 客户端回调通道字典 /// key:IP /// value:回调通道 /// private DictionaryClient_Callback_Dic = new Dictionary (); /// /// 客户端回调字典锁 /// private static object ClientLock = new object(); #endregion //构造函数 public DuplexMessageService() { } #region IDuplexMessageService 成员 ////// 订阅报警,当报警产生时会对客户端进行推送 /// ///客户端唯一标识符 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())) { /*如果通道和原来的不一样,客户端重启了*/ Client_Callback_Dic[ip] = OperationContext.Current.GetCallbackChannel (); try { //客户端报警推送 //Client_Callback_Dic[ip].Leave(ip); } catch { } } } else { Client_Callback_Dic.Add(ip, OperationContext.Current.GetCallbackChannel ()); try { //客户端报警推送 //Client_Callback_Dic[ip].Leave(ip); } catch { } } } } /// /// 取消订阅,取消之后不推送 /// /// 客户端唯一标识符 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; } } } ////// 客户端发往服务端加入信息 /// /// 客户端唯一标识符 /// 客户端发来的信息,如果需要传送信息使用json格式传送 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(); if (Client_Callback_Dic.ContainsKey(ip)) { if (!Client_Callback_Dic[ip].Equals(callBack)) { Client_Callback_Dic[ip] = OperationContext.Current.GetCallbackChannel (); } } 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{ ////// 功 能:WCF服务控制类 /// 创 建 人:龚安川 /// 创建时间:2017-02-24 /// public class ServerManagement { ////// 服务开启 /// 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(); } ////// 服务停止 /// 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{ ////// 功 能:WCF服务工厂类 /// 创 建 人:龚安川 /// 创建时间:2017-02-24 /// public class ServiceHostFactory { #region [变量定义] ////// 日志管理对象 /// private ILog log = LogManager.GetLogger(typeof(ServiceHostFactory)); ////// 此处必须用单例,防止多次开启导致异常 /// private static ServiceHostFactory instance = null; ////// 对象锁 /// private static object padlock = new object(); ////// 服务列表 /// private Dictionarym_HostDic; /// /// 服务类型集合 /// private ListServices = new List (); /// /// 标识服务是否开启 /// 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(); //初始化服务 InitServices(); GetCfg(); } /// /// 析构函数 /// ~ServiceHostFactory() { StopAllService(); } #region [公开方法] ////// 开启所有服务 /// 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; } } } ////// 关闭所有服务 /// 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(); behavior.IncludeExceptionDetailInFaults = true; return host; } #endregion }}
配置文件App.config,利用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{ ////// 功 能:WCFClient 对象 /// 创 建 人:龚安川 /// 创建时间:2017-02-24 /// public class WCFClient { #region [变量定义] #region [Static] ////// WCF客户端对象-单例模式 /// static WCFClient instance = null; static object padlock = new object(); ////// 实例化客户端代理-单例模式; /// public static WCFClient Instance { get { if (instance == null) { lock (padlock) { if (instance == null) { instance = new WCFClient(); } } } return instance; } } #endregion ////// 服务终结点集合对象 /// private Dictionary_EndpointNameDic; #endregion private WCFClient() { InitEndpointNameDic(); } #region [公开方法] /// /// 获取WCF客户端代理-单向通讯 /// ///契约类型 ///public T GetService () { return ServiceProxyFactory.Create (_EndpointNameDic[typeof(T).Name]); } /// /// 获取WCF客户端代理-双向通讯 /// ////// /// public T GetService (object context) { return ServiceProxyFactory.Create (context, _EndpointNameDic[typeof(T).Name]); } #endregion #region [私有方法] //初始化终结点集合 private void InitEndpointNameDic() { _EndpointNameDic = new Dictionary (); _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{ ////// 功 能:服务代理工厂 /// 创 建 人:龚安川 /// 创建时间:XXXX /// public static class ServiceProxyFactory { #region [公开方法] ////// 创建到指定终结点地址的指定类型的通道; /// ///由通道工厂生成的通道类型 /// 用于终结点的配置名称 ///public static T Create (string endpointName) { if (string.IsNullOrEmpty(endpointName)) { throw new ArgumentNullException("endpointName"); } //通过自定义的RealProxy创建TransparentProxy供客户端代码调用,统一管理调用异常记录 return (T)(new ServiceRealProxy (endpointName).GetTransparentProxy()); } /// /// 创建到指定终结点地址的指定类型的通道; /// 在服务和客户端上的回调实例之间创建双工通道; /// ///由通道工厂生成的通道类型 /// 客户端用以侦听来自所连接服务的消息 /// 用于终结点的配置名称 ///public static T Create (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 (callbackObject, endpointName).GetTransparentProxy(); } /// /// 创建到指定终结点地址的指定类型的通道; /// 在服务和客户端上的回调实例之间创建双工通道; /// 调用结束后根据指定决定是否立即释放通道; /// ///由通道工厂生成的通道类型 /// 客户端用以侦听来自所连接服务的消息 /// 用于终结点的配置名称 /// 是否立即释放 ///public static T Create (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 (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{ ////// 功 能:自定义单通道真实代理 /// 创 建 人:龚安川 /// 创建时间:XXXX /// 说明:RealProxy中加入异常捕获、记录日志等非业务逻辑代码 /// 这些代码在每次调用服务端方法时都会被调用 /// internal class ServiceRealProxy: RealProxy { #region [变量定义] /// /// 终结点名称 /// 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(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{ ////// 功 能:自定义双工通道真实代理; /// 创 建 人:龚安川 /// 创建时间: /// ///internal class DuplexServiceRealProxy : RealProxy { #region [变量定义] /// /// 通道集合 /// private static Hashtable channels = new Hashtable(); ////// 终结点名称 /// private string _endpointName; ////// 回调对象 /// private object _callbackObject; ////// 通道是否已释放 /// 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(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 (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{ ////// 功 能:ChannelFactory单通道静态工厂类 /// 创 建 人:龚安川 /// 创建时间:XXXX /// internal static class ChannelFactoryCreator { ////// 通道工厂集合对象 /// private static Hashtable channelFactories = new Hashtable(); ////// 通道创建 /// ///契约类型 /// 终结点名称 ///public static ChannelFactory Create (string endpointName) { if (string.IsNullOrEmpty(endpointName)) { throw new ArgumentNullException("endpointName"); } ChannelFactory channelFactory = null; //判断当前终结点是否存在 if (channelFactories.ContainsKey(endpointName)) { //返回当前通道工厂 channelFactory = channelFactories[endpointName] as ChannelFactory ; } if (channelFactory == null) { //创建新的通道工厂 channelFactory = new ChannelFactory (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 /// internal static class DuplexChannelFactoryCreator { ////// 通道工厂集合对象 /// private static Hashtable channelFactories = new Hashtable(); public static DuplexChannelFactoryCreate (object callbackObject, string endpointName) { if (callbackObject == null) { throw new ArgumentNullException("callbackObject"); } if (string.IsNullOrEmpty(endpointName)) { throw new ArgumentNullException("endpointName"); } DuplexChannelFactory channelFactory = null; //判断当前终结点是否存在 if (channelFactories.ContainsKey(endpointName)) { //返回当前通道工厂 channelFactory = channelFactories[endpointName] as DuplexChannelFactory ; } if (channelFactory == null) { channelFactory = new DuplexChannelFactory (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{ ////// 功 能:终结点名字 /// 创 建 人:龚安川 /// 创建时间:XXXX /// public static class EndpointNames { ////// 计算服务 /// public const string CalculatorService = "CalculatorService"; ////// 双向通讯服务 /// 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{ ////// 功 能:客户端回调服务实现类 /// 创 建 人:龚安川 /// 创建时间: /// public class DuplexMessageCallBack : IDuplexMessageCallBack { #region [变量定义] ////// 唯一实例 /// private static DuplexMessageCallBack _Instance; ////// 客户端回调对象锁 /// private static object lockObj = new object(); ////// 心跳线程 /// private Thread HeadtThread = null; ////// 订阅线程 /// private Thread SubscribeThread = null; ////// 是否心跳检测 /// private bool isHearting = false; ////// 是否订阅 /// private bool isSubscribe = false; #endregion public DuplexMessageCallBack() { } ////// 获取唯一实例 /// ///public static DuplexMessageCallBack GetInstance() { lock (lockObj) { if (_Instance == null) _Instance = new DuplexMessageCallBack(); return _Instance; } } #region [委托和事件定义] /// /// 收到问号委托 /// /// public delegate void DelegateGreet(string messages); ////// 收到离开委托 /// /// public delegate void DelegateLeave(string ip); ////// 收到报警事件 /// public event DelegateGreet OnGreet; ////// 恢复报警事件 /// 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 [公开方法] ////// 订阅报警 /// 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(); } } ////// 取消报警订阅 /// 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(context).UnSbuscribeAlarm(); } catch { } isSubscribe = false; } } #endregion #region [私有方法] //订阅定时器事件 private void Subscribing() { InstanceContext context = new InstanceContext(this); while (!isSubscribe) { try { //客户端请求报警 WCFClient.Instance.GetService (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 (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().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 (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))); } }}
演示结果如下: