WCF全双工数据传输
项目结构:
客户端:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Runtime.Serialization.Formatters.Binary; using System.IO; using System.Runtime.Serialization; using System.Collections; using Cn100.Wcf.Duplex.Contract; using Cn100.Wcf.Duplex.Service; using Cn100.Wcf.Duplex.Common; using System.ServiceModel.Description; namespace Cn100.Wcf.Duplex.Client { public delegate void DuplexCallBack(object reust,int count); public class ClientHandler : ICallback,IDisposable { /// <summary> /// 处理回调事件的类 /// </summary> public static event DuplexCallBack callback; public Hashtable hashResult = new Hashtable(); public StringBuilder builder = new StringBuilder(); private static DuplexChannelFactory<ICallbackService> channelFactory = null; /// <summary> /// 接收回传数据 /// </summary> /// <param name="result"></param> /// <param name="count"></param> /// <param name="isComplete"></param> public void ReceiveData(object result, int count, int index, bool isComplete) { if (isComplete) { //ArrayList array = new ArrayList(hashResult.Keys); //array.Sort(); //StringBuilder builder = new StringBuilder(); //foreach (int key in array) //{ // builder.Append(hashResult[key].ToString()); //} object obj = new object(); if (builder.Length > 0) { obj = SerializeHelper.DeserializeObject(builder.ToString()); builder = new StringBuilder(); } else { obj = result; } if (callback != null) { callback(obj, count); //callback = null; } HideProcess(); } else { builder.Append(result.ToString()); //hashResult.Add(index, result); } } /// <summary> /// 显示进度条 /// </summary> public void ShowProcess() { } /// <summary> /// 隐藏进度条 /// </summary> public void HideProcess() { } public static object @Lock = new object(); /// <summary> /// 发送数据到服务器端 /// </summary> /// <param name="Assemply">服务端程序集名称,如:Cn100.Wcf.Duplex.Service.CallbackService,Cn100.Wcf.Duplex.Service</param> /// <param name="method">调用方法名称,如:SendData</param> /// <param name="parameters">调用方法参数</param> public void SendData(string assembly, string method, params object[] parameters) { object[] paras=new object[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { paras[i] = SerializeHelper.SerializeObject(parameters[i]); } lock (@Lock) { try { if (channelFactory == null || channelFactory.State != CommunicationState.Opened) { channelFactory = CreateChannel<ICallbackService>("CallBackService", new ClientHandler()); } ICallbackService proxy = channelFactory.CreateChannel(); proxy.SendData(assembly, method, paras); Console.Read(); } catch (Exception ex) { channelFactory = null; //throw ex; } } //using (DuplexChannelFactory<ICallbackService> channelFactory = new DuplexChannelFactory<ICallbackService>(instanceContext, "CallBackService")) //{ // ICallbackService proxy = channelFactory.CreateChannel(); // using (proxy as IDisposable) // { // ShowProcess(); // proxy.SendData(assembly, method, paras); // Console.Read(); // } //} } /// <summary> /// 异步调用发送数据 /// </summary> /// <param name="assembly"></param> /// <param name="method"></param> /// <param name="parameters"></param> public void AsySendData(string assembly, string method, params object[] parameters) { Action<string, string,object[]> action = (ass, m, p) => SendData(ass, m, p); action.BeginInvoke(assembly, method, parameters, ar => action.EndInvoke(ar), null); } public DuplexChannelFactory<T> CreateChannel<T>(string name,object handler) { InstanceContext instanceContext = new InstanceContext(handler); DuplexChannelFactory<T> factory = new DuplexChannelFactory<T>(instanceContext, name); //EndpointAddress address = new EndpointAddress("net.tcp://127.0.0.1:9998/CallbackService"); //NetTcpBinding binding = new NetTcpBinding(); //ContractDescription description=new ContractDescription(name); //ServiceEndpoint potion=new ServiceEndpoint (description,binding,address); //DuplexChannelFactory<T> factory = new DuplexChannelFactory<T>(instanceContext, potion); return factory; } public void Dispose() { if (channelFactory != null) { channelFactory.Close(); channelFactory = null; } } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Cn100.Wcf.Duplex.Client { interface IClientHandler { /// <summary> /// 显示进度条 /// </summary> void ShowProcess(); /// <summary> /// 隐藏进度条 /// </summary> void HideProcess(); /// <summary> /// 发送数据到服务器端 /// </summary> /// <param name="Assemply">服务端程序集名称,如:Cn100.Wcf.Duplex.Service.CallbackService,Cn100.Wcf.Duplex.Service</param> /// <param name="method">调用方法名称,如:SendData</param> /// <param name="parameters">调用方法参数</param> void SendData(string assembly, string method, params object[] parameters); } }
协议:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; namespace Cn100.Wcf.Duplex.Contract { [ServiceContract(Namespace = "http://www.cn100.com", CallbackContract = typeof(ICallback),SessionMode=SessionMode.Required)] public interface ICallbackService { /// <summary> /// 发送数据到服务器端 /// </summary> /// <param name="Assemply">服务端程序集名称,如:Cn100.Wcf.Duplex.Service.CallbackService,Cn100.Wcf.Duplex.Service</param> /// <param name="method">调用方法名称,如:SendData</param> /// <param name="parameters">调用方法参数</param> [OperationContract(IsOneWay = true)] void SendData(string Assemply, string method, params object[] parameters); } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; namespace Cn100.Wcf.Duplex.Contract { [ServiceContract] public interface ICallback { /// <summary> /// 客户端接收数据 /// </summary> /// <param name="result"></param> /// <param name="count"></param> /// <param name="isComplete"></param> [OperationContract(IsOneWay = true)] void ReceiveData(object result, int count, int index, bool isComplete); } }
服务端
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Reflection; using System.Diagnostics; using System.Runtime.Serialization; using System.IO; using System.Text.RegularExpressions; using Cn100.Wcf.Duplex.Contract; using Cn100.Wcf.Duplex.Module; using System.Runtime.Serialization.Formatters.Binary; using Cn100.Wcf.Duplex.Common; namespace Cn100.Wcf.Duplex.Service { [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)] public class CallbackService:ICallbackService { public void SendData(string assembly, string method, params object[] parameters) { try { string strPath = System.Environment.CurrentDirectory; string[] assemplys = assembly.Split(','); string strClass = assemplys[0]; string strDLL = strPath + "/" + assemplys[1] + ".dll"; Assembly ass = Assembly.LoadFile(strDLL); Type type = ass.GetType(strClass); MethodInfo mi = type.GetMethod(method); object obj = ass.CreateInstance(strClass); object[] paras = new object[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { paras[i] = SerializeHelper.DeserializeObject(parameters[i]); } object result = mi.Invoke(obj, paras); ICallback callback = OperationContext.Current.GetCallbackChannel<ICallback>(); if (result != null) { int intOut = 0; if (paras.Length > 0) { string strOut = paras[paras.Length - 1].ToString(); int.TryParse(strOut, out intOut); } //序列化 string strResult = SerializeHelper.SerializeObject(result); string[] source = StingSplit.SplitData(strResult, Size); for (int i = 0; i < source.Length; i++) { callback.ReceiveData(source[i], i, i, false); } callback.ReceiveData("", intOut, source.Length, true); } else { callback.ReceiveData(null, 0, 0, true); } } catch (Exception ex) { Console.WriteLine(ex.Message); if (ex.InnerException != null) { Console.WriteLine(ex.InnerException.Message); } throw ex; } } const string TransferSizeConfigName = "transferSize"; public static int Size = GetTransferSize(); public static int GetTransferSize() { if (System.Configuration.ConfigurationManager.AppSettings[TransferSizeConfigName] != null) { string strSize = System.Configuration.ConfigurationManager.AppSettings[TransferSizeConfigName].ToString(); int intSize = 0; if (int.TryParse(strSize, out intSize)) { return intSize; } } return 20; } } }
服务载体配置:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <bindings> <netTcpBinding> <binding name="DuplexBindingConfiguration" openTimeout="00:30:00" receiveTimeout="01:30:00" sendTimeout="01:30:00" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647"> <security mode="None" /> <readerQuotas maxStringContentLength="6553600" maxArrayLength="6553600" /> </binding> </netTcpBinding> </bindings> <behaviors> <serviceBehaviors> <behavior name="mybehavior"> <serviceDebug includeExceptionDetailInFaults="true"/> </behavior> </serviceBehaviors> </behaviors> <services> <service name="Cn100.Wcf.Duplex.Service.CallbackService"> <endpoint address="net.tcp://127.0.0.1:9998/CallbackService" bindingConfiguration="DuplexBindingConfiguration" binding="netTcpBinding" contract="Cn100.Wcf.Duplex.Contract.ICallbackService" /> </service> </services> </system.serviceModel> <!--传输大小配置,不通超过最大配置:8192--> <appSettings> <add key="transferSize" value="500"/> </appSettings> </configuration>
客户端配置:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <bindings> <netTcpBinding> <binding name="TicketBindingConfiguration" openTimeout="00:30:00" receiveTimeout="01:30:00" sendTimeout="01:30:00" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647"> <security mode="None" /> <readerQuotas maxStringContentLength="6553600" maxArrayLength="6553600" /> </binding> </netTcpBinding> </bindings> <client> <endpoint name="CallBackService" bindingConfiguration="TicketBindingConfiguration" address="net.tcp://127.0.0.1:9998/CallbackService" binding="netTcpBinding" contract="Cn100.Wcf.Duplex.Contract.ICallbackService"/> </client> <!--address="net.tcp://192.168.0.8:5687/CallbackService"--> <!--address="net.tcp://127.0.0.1:9998/CallbackService"--> </system.serviceModel> </configuration>
工具类:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.Serialization; using System.IO; using System.Runtime.Serialization.Formatters.Binary; using Newtonsoft.Json; using System.Diagnostics; namespace Cn100.Wcf.Duplex.Common { public class SerializeHelper { /// <summary> /// 反序列化对象 /// </summary> /// <param name="source"></param> /// <returns></returns> public static object DeserializeObject(object source) { //Stopwatch watch = new Stopwatch(); //watch.Start(); IFormatter formatter = new BinaryFormatter(); byte[] buffer = Convert.FromBase64String(source.ToString()); MemoryStream stream = new MemoryStream(buffer); object obj = formatter.Deserialize(stream); stream.Flush(); stream.Close(); //long time = watch.ElapsedMilliseconds; //watch.Stop(); //Console.WriteLine(time); return obj; } /// <summary> /// 序列化对象 /// </summary> /// <param name="source"></param> /// <returns></returns> public static string SerializeObject(object source) { //Stopwatch watch = new Stopwatch(); //watch.Start(); IFormatter formatter = new BinaryFormatter(); MemoryStream stream = new MemoryStream(); formatter.Serialize(stream, source); stream.Position = 0; byte[] buffer = new byte[stream.Length]; stream.Read(buffer, 0, buffer.Length); stream.Flush(); stream.Close(); string strResult = Convert.ToBase64String(buffer); //long time = watch.ElapsedMilliseconds; //watch.Stop(); //Console.WriteLine(time); return strResult; } public static string SerializeObjectToJson(object source) { return JsonConvert.SerializeObject(source); } public static object DeserializeJsonToObject(string source) { return JsonConvert.DeserializeObject(source); } public static List<T> DeserializeJsonToObject<T>(string source) { return JsonConvert.DeserializeObject<List<T>>(source); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; namespace Cn100.Wcf.Duplex.Common { public class StingSplit { public static string[] SplitData(string source, int length) { List<string> lists = new List<string>(); if (source.Length > length) { Regex reg = new Regex(".{" + length.ToString() + "}", RegexOptions.Multiline | RegexOptions.IgnoreCase); if (reg.IsMatch(source)) { int i = 0; foreach (var m in reg.Matches(source)) { i++; lists.Add(m.ToString()); } if (i * length < source.Length) { lists.Add(source.Substring(i * length)); } } } else { lists.Add(source); } return lists.ToArray(); } } }