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
總結
以上是生活随笔為你收集整理的WCF 服务端+客户端动态调用的全部內容,希望文章能夠幫你解決所遇到的問題。