WCF本質上提供一個跨進程、跨機器以致跨網路的服務調用 ,本樣本中 主要實現了計算機的功能,大部分的函數來源於網上別人的文章,這叫站在巨人的肩膀上,O(∩_∩)O哈哈~,但是為了加深自己的對wcf的理解,因此決定自己在寫個類似的demo,把寫demo中遇到的問題展現出來,我相信對於初識wcf的程式員了來說,也會遇到各種問題。好了步入正題。
WCF 分為四個部分 1、契約(Interface)2、服務契約(Service)3、WCF 宿主程式(控制台或者IIS) 4、用戶端(Client)
本人在寫wcf的時候喜歡將四個部分分別拆分成不同的類庫來管理。
1、契約
契約是對外開放的介面,暴露給用戶端的函數名稱首先,建立一個wcf服務庫,名稱為Interface,:
刪掉程式中預設的檔案(App.config,IService1.cs,Service1.cs),建立ICalculator
namespace Interface{ [ServiceContract(Name = "CalculatorService", Namespace = "")]public interface ICalculator { [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); }}
2、建立服務契約,其實就是實現契約的類 ,添加"建立項目"->WCF->"WCF服務庫" (如 第一步建立契約是一樣的步驟),類庫的名稱為Service,
建立成功後,刪除預設檔案(App.config,IService1.cs,Service1.cs),添加引用"Interface" 類庫,建立CalculatorService 並實現ICalculator
namespace Service{ [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)]public class CalculatorService : ICalculator {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; } }
3、建立WCF宿主程式,WCF的契約和服務契約都已經建完,但是還需要一個程式來承載這些介面供外部程式調用,因此稱之為宿主程式,宿主程式可以部署在IIS上,也可以用控制台程式運行方便調試,下面我就介紹用控制台程式作為宿主程式。在解決方案的名稱右鍵添加"控制台程式" 名稱為Hosting ,添加對Interface和Service的引用
實現wcf對外開放有兩種方式,一種方式是設定檔,將所有的WCF資訊都寫到設定檔中
第一種:WCF的相關資訊寫到設定檔(App.config)中
<?xml version="1.0" encoding="utf-8"?><configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/> </startup> <system.serviceModel> <services> <service name="Service.CalculatorService" behaviorConfiguration="WCFService.WCFServiceBehavior"> <endpoint address="http://127.0.0.1:8888/CalculatorService" binding="wsHttpBinding" contract="Service.Interface.ICalculator"> </endpoint> <endpoint address="net.tcp://127.0.0.1:9999/CalculatorService" binding="netTcpBinding" contract="Service.Interface.ICalculator"> </endpoint> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" /> <host> <baseAddresses> <add baseAddress="http://127.0.0.1:8888/"/> <add baseAddress="net.tcp://127.0.0.1:9999/"/> </baseAddresses> </host> </service> </services> <behaviors> <serviceBehaviors> <behavior name="WCFService.WCFServiceBehavior"> <serviceMetadata httpGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="false" /> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel></configuration>
Main方法的主要代碼如下,
public class Program {internal static ServiceHost host = null;static void Main(string[] args) {using (ServiceHost host = new ServiceHost(typeof(CalculatorService))) { host.Opened += delegate{ Console.WriteLine("CalculaorService已經啟動,按任意鍵終止服務!"); }; host.Open(); Console.ReadLine(); } } }
運行控制台程式,如所示:
第二種:寫一個WCFHelper類,添加協助類的時候,一定引用System.Runtime.Serialization ,並且刪掉App.config 對wcf的配置,否則會報錯代碼如下:
public static class WCFHelper {public static string IP { get; set; }public static int Port { get; set; }static WCFHelper() {try{ IP = System.Environment.MachineName;// ConfigurationManager.AppSettings["ServiceIP"];// Port = int.Parse(ConfigurationManager.AppSettings["ServicePort"]); }catch{ } EndpointAddress.Add(BindingProtocol.Http, "http://{0}:{1}/{2}/{3}"); EndpointAddress.Add(BindingProtocol.NetTcp, "net.tcp://{0}:{1}/{2}/{3}");//EndpointAddress.Add(BindingProtocol.NetMsmq, "net.msmq://{0}:{1}/" + schema + "/{2}");//EndpointAddress.Add(BindingProtocol.NetPipe, "net.pipe://{0}:{1}/" + schema + "/{2}");try{ httpBinding = new BasicHttpBinding(); httpBinding.MaxBufferSize = 200065536; httpBinding.MaxReceivedMessageSize = 200065536; httpBinding.CloseTimeout = new TimeSpan(0, 10, 0); httpBinding.OpenTimeout = new TimeSpan(0, 10, 0); httpBinding.ReceiveTimeout = new TimeSpan(0, 10, 0); httpBinding.SendTimeout = new TimeSpan(0, 1, 0); httpBinding.Security.Mode = BasicHttpSecurityMode.None; httpBinding.ReaderQuotas.MaxDepth = 64; httpBinding.ReaderQuotas.MaxStringContentLength = 2147483647; httpBinding.ReaderQuotas.MaxArrayLength = 16384; httpBinding.ReaderQuotas.MaxBytesPerRead = 4096; httpBinding.ReaderQuotas.MaxNameTableCharCount = 16384; }catch{ httpBinding = new BasicHttpBinding(); }try{ tcpBinding = new NetTcpBinding(); tcpBinding.CloseTimeout = new TimeSpan(0, 1, 0); tcpBinding.OpenTimeout = new TimeSpan(0, 1, 0); tcpBinding.ReceiveTimeout = new TimeSpan(0, 10, 0); tcpBinding.SendTimeout = new TimeSpan(0, 1, 0); tcpBinding.TransactionFlow = false; tcpBinding.TransferMode = TransferMode.Buffered; tcpBinding.TransactionProtocol = TransactionProtocol.OleTransactions; tcpBinding.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard; tcpBinding.ListenBacklog = 100000; tcpBinding.MaxBufferPoolSize = 524288; tcpBinding.MaxBufferSize = 200065536; tcpBinding.MaxConnections = 2000; tcpBinding.MaxReceivedMessageSize = 200065536; tcpBinding.PortSharingEnabled = true; tcpBinding.Security.Mode = SecurityMode.None; tcpBinding.ReaderQuotas.MaxDepth = 64; tcpBinding.ReaderQuotas.MaxStringContentLength = 2147483647; tcpBinding.ReaderQuotas.MaxArrayLength = 163840000; tcpBinding.ReaderQuotas.MaxBytesPerRead = 4096; tcpBinding.ReaderQuotas.MaxNameTableCharCount = 16384; tcpBinding.ReliableSession.Ordered = true; tcpBinding.ReliableSession.InactivityTimeout = new TimeSpan(0, 10, 0); tcpBinding.ReliableSession.Enabled = false; }catch{ tcpBinding = new NetTcpBinding(); } }private static BasicHttpBinding httpBinding;public static BasicHttpBinding HttpBinding {get{return httpBinding; } }private static NetTcpBinding tcpBinding;public static NetTcpBinding TcpBinding {get{return tcpBinding; } }public static EndpointAddress GetEndpointAddress(string ip, int port, string serviceSchema, string serviceName, BindingProtocol protocol) {string address = EndpointAddress[protocol]; address = string.Format(address, ip, port, serviceSchema, serviceName);return new EndpointAddress(address); }public static EndpointAddress GetEndpointAddress(string serviceSchema, string serviceName, BindingProtocol protocol) {string address = EndpointAddress[protocol]; address = string.Format(address, IP, Port, serviceSchema, serviceName);return new EndpointAddress(address); }public static Uri GetBaseAddress(string ip, int port, string serviceSchema, string serviceName, BindingProtocol protocol) {string address = EndpointAddress[protocol]; address = string.Format(address, ip, port + 1, serviceSchema, serviceName);return new Uri(address); }public static Uri GetBaseAddress(string serviceSchema, string serviceName, BindingProtocol protocol) {string address = EndpointAddress[protocol]; address = string.Format(address, IP, Port + 1, serviceSchema, serviceName);return new Uri(address); }private readonly static Dictionary<BindingProtocol, string> EndpointAddress = new Dictionary<BindingProtocol, string>();public enum BindingProtocol { Http = 1, NetTcp = 2,//NetPipe = 3,//NetMsmq = 4 } }
將 宿主程式中的main方法改成如下:
static void Main(string[] args) { WCFHelper.IP = "127.0.0.10"; WCFHelper.Port = 9999; Uri httpUri = WCFHelper.GetBaseAddress(WCFHelper.IP, WCFHelper.Port, "Calculator", "Inspect", WCFHelper.BindingProtocol.Http);using (ServiceHost host = new ServiceHost(typeof(CalculatorService), httpUri)) { host.AddServiceEndpoint(typeof(ICalculator), WCFHelper.TcpBinding, WCFHelper.GetEndpointAddress(WCFHelper.IP, WCFHelper.Port, "Calculator", "InspectService", WCFHelper.BindingProtocol.NetTcp).Uri.AbsoluteUri); ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();#if DEBUGbehavior.HttpGetEnabled = true;#elsebehavior.HttpGetEnabled = false;#endifhost.Description.Behaviors.Add(behavior); host.Opened += delegate{ Console.WriteLine("CalculaorService已經啟動,按任意鍵終止服務!"); }; host.Open(); Console.ReadLine(); }}
4、宿主程式建完了,WCF的所有鍥約已經對外可以訪問了,那現在需要建立Client 去調用wcf程式了 ,
首先,編譯宿主程式Host,找到bin/debug/ Host.exe 右鍵管理員開啟,如果如證明服務已經於寧
其次,在解決方案名稱->右鍵添加Winform程式 建立WinForm 程式,添加"服務引用"
加入服務參考 (此引用方式,是以"3、添加宿主程式" ->"第二種 WCF協助類的方式")
點擊“轉到” 系統發現WCF服務,如所示:
點擊確定,就成功的將wcf的引用了,下面開始調用WCF的程式了,例如調用Add方法,代碼如下:
ServiceReference1.CalculatorServiceClient client = new ServiceReference1.CalculatorServiceClient(); textBox3.Text = client.Add(double.Parse(textBox1.Text), double.Parse(textBox2.Text)).ToString();
到此為止,wcf程式已經完成的跑通了,記得 調試的時候已經把宿主程式和client程式同時運行。
如果閑麻煩,vs 支援同時啟動多重專案,可以同時把client 和host同時啟動