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);//    }//}#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();}}
}
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;#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}
}
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;#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;}}
}
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;#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}
}
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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/269933.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

动态区间第K大

整体二分。 主要需要注意的一点是&#xff0c;对于每个删除操作&#xff0c;若删除操作被算入贡献&#xff0c;则最开始的插入操作也一定会被算入&#xff0c;所以不必担心删除删错。 #include<cstdio> #include<algorithm> #include<cstring> using namesp…

Docker笔记:docker四种网络模式介绍

目录 1、docker网络模式分类 2、bridge 网桥模式 3、host 模式 4、container模式 5、none模式

Elasticsearch之分词器的工作流程

前提 什么是倒排索引&#xff1f; Elasticsearch之分词器的作用 Elasticsearch的分词器的一般工作流程&#xff1a; 1、切分关键词 2、去除停用词 3、对于英文单词&#xff0c;把所有字母转为小写&#xff08;搜索时不区分大小写&#xff09; 后续博客 Elasticsearch之停用词 转…

shell 1

换行符号 ctrlJBackspace ctrlHend-of-file ctrlD 1 背景知识2 入门 第一行 #! 空白符号被略过 可选项 注意此处的初级陷阱p37 shell基本元素 命令与参数 如果使用的使"&"而不是";"&#xff0c;则Shell将在后台执行其前面的命令&#xff1b;意味着&…

微信小程序把玩(二十六)navigator组件

微信小程序把玩&#xff08;二十六&#xff09;navigator组件 原文:微信小程序把玩&#xff08;二十六&#xff09;navigator组件navigator跳转分为两个状态一种是关闭当前页面一种是不关闭当前页面。用redirect属性指定。 主要属性&#xff1a; wxml <navigator url".…

docker笔记:docker容器通信参数 --link参数介绍

目录 1、link 参数作用 2、命令格式 3、link原理 4、测试案例 5、link参数注意事项

JavaScript 经典实例日常收集整理(常用经典)

作者&#xff1a;阿讯小飞 原文来自&#xff1a;脚本之家 跨浏览器添加事件 //跨浏览器添加事件 function addEvent(obj,type,fn){ if(obj.addEventListener){ obj.addEventListener(type,fn,false); }else if(obj.attachEvent){//IE obj.attchEvent(ontype,fn); } } 跨浏览器移…

超轻量级DI容器框架Google Guice与Spring框架的区别教程详解及其demo代码片段分享...

超轻量级DI容器框架Google Guice与Spring框架的区别教程详解及其demo代码片段分享 DI框架 Google-Guice入门介绍转载于:https://www.cnblogs.com/jing1617/p/6473339.html

equals与hashcode的区别与联系

1.""与equals的区别与联系 &#xff08;1&#xff09;“”对于基本数据类型&#xff0c;只要值相等&#xff0c;就返回true&#xff0c;否则返回false。 若比较的为对象&#xff0c;则判断的是两个对象是否处于同一个内存地址。 &#xff08;2&#xff09;equals为O…

BZOJ1922 SDOI2010 大陆争霸 最短路

题意&#xff1a;给定一个图&#xff0c;图中有保护关系(u,v)表示到v之前必须先到一次u&#xff0c;求从1到N的最短路 题解&#xff1a; 定义d1[i]为直接到达i的最短距离&#xff0c;这个的更新和普通的Dijkstra一样 定义d2[i]为解除i的所有保护的最短距离&#xff08;不一定要…

HashSet和LinkedHashSet使用

Set接口 HashSet接口 散列是一种常见的数据存储模式&#xff0c;HashSet是基于散列存放的集合。本博客主要讲解HashSet子类的继承特点以及存储特点。 HashSet是Set接口较为常见的一个子类&#xff0c;该子类的最大特点是不允许保存重复元素&#xff0c;并且所有的内容都采用散列…

bzoj4278[ONTAK2015]Tasowanie bzoj1692[USACO 2007Dec]队列变换(Best Cow Line) 贪心正确性证明...

做法网上到处都有就不说了. 这题其实是之前做的….不过由于人太傻现在才想明白比较字典序进行贪心的正确性…. 方便起见,在两个串的最右端都加上很大但不相同的字符,避免第lcp1个字符不存在的边界。 如果两个串当前最左端的字符不相同显然选较小的. 否则,设两个剩下的串的lcp长…

MySQL约束和修改数据表知识集结

一、约束   划分标准&#xff1a;功能、数据列的数目 功能&#xff1a; &#xff08;1&#xff09;NOT NULL&#xff08;非空约束&#xff09; &#xff08;2&#xff09;PRIMARY KEY&#xff08;主键约束&#xff09; &#xff08;3&#xff09;UNIQUE&#xff08;唯一约束&…

C++注意事项

1. 空字符为NULL,大写转载于:https://www.cnblogs.com/--CYH--/p/6481691.html

论文信息系统项目管理的进度管理

论信息系统项目管理的进度管理 摘要&#xff1a; 2018 年 12 月&#xff0c;我负责了广东省某电力企业“基于 VR 的电力作业仿真培训系统”的项目建设&#xff0c;担任项目经理一职。电力作业技能培训是电力人员上岗前确保安全和保证质量的关键一环&#xff0c;由于传统的电力…