之前在公司用的服務端是wcf寫的,但是沒有深入研究,最近找工作,面試的時候好多人看到這個總提問,這裡做個複習 就用微軟官方上的例子,搭一個簡單的wcf服務,分6步 1 定義服務協定也就是契約,其實就是定義一個服務介面,這玩意後邊是公開客戶端用的,然後也告訴後邊承載程式應該如何載入服務 主要涉及兩個特 ...
之前在公司用的服務端是wcf寫的,但是沒有深入研究,最近找工作,面試的時候好多人看到這個總提問,這裡做個複習
就用微軟官方上的例子,搭一個簡單的wcf服務,分6步
1 定義服務協定也就是契約,其實就是定義一個服務介面,這玩意後邊是公開客戶端用的,然後也告訴後邊承載程式應該如何載入服務
主要涉及兩個特性:一個是ServiceContract(介面的特性,定義這個是服務契約,裡邊又一些設置參數可以設置一下),OperationContract設置介面的方法的,如果不設置,方法就不會唄公開
這裡是直接新建的wcf的服務程式,vs自動給生成了一個介面,就直接在這個裡邊添加了幾個計算的介面函數了
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.ServiceModel.Web; using System.Text; namespace GettingStartedLib { // 註意: 使用“重構”菜單上的“重命名”命令,可以同時更改代碼和配置文件中的介面名“IService1”。 //協定 [ServiceContract(//CallbackContract =typeof(ICallBack),//雙工時的返回協定 ConfigurationName = "Calculator",//配置文件重的服務名 Name = "ICalculator",//webservice描述文件重的portType名 Namespace ="http://SampleWcfTest",//webservice描述文件重的portType命名空間 ProtectionLevel =System.Net.Security.ProtectionLevel.EncryptAndSign,//保護等級 SessionMode =SessionMode.Allowed)]//設置會話的支持模式 public interface ICalculator { [OperationContract] string GetData(int value); [OperationContract] CompositeType GetDataUsingDataContract(CompositeType composite); //定義方法的操作,帶了該特性才會被公佈 [OperationContract] double Add(double n1, double n2); [OperationContract] double Subtract(double n1, double n2); [OperationContract] double Multiply(double n1, double n2); [OperationContract] double Divide(double n1, double n2); // TODO: 在此添加您的服務操作 } public interface ICallBack { [OperationContract(IsOneWay = true)] void Reply(string responseToGreeting); } // 使用下麵示例中說明的數據約定將複合類型添加到服務操作。 [DataContract] public class CompositeType { bool boolValue = true; string stringValue = "Hello "; [DataMember] public bool BoolValue { get { return boolValue; } set { boolValue = value; } } [DataMember] public string StringValue { get { return stringValue; } set { stringValue = value; } } } }
2 實現上邊的介面,vs自動生成了svc文件和對應的svc.cs文件,直接雙擊就是對應的實現類了,吧介面實現就好了,這個svc文件右鍵用文本編輯器打開可以進行編輯,如果後邊用IIS載入的時候需要修改裡邊的東西
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.ServiceModel.Web; using System.Text; namespace GettingStartedLib { // 註意: 使用“重構”菜單上的“重命名”命令,可以同時更改代碼、svc 和配置文件中的類名“Service1”。 // 註意: 為了啟動 WCF 測試客戶端以測試此服務,請在解決方案資源管理器中選擇 Service1.svc 或 Service1.svc.cs,然後開始調試。 public class Calculator : ICalculator { public string GetData(int value) { return string.Format("You entered: {0}", value); } public CompositeType GetDataUsingDataContract(CompositeType composite) { if (composite == null) { throw new ArgumentNullException("composite"); } if (composite.BoolValue) { composite.StringValue += "Suffix"; } return composite; } public double Add(double n1, double n2) { double result = n1 + n2; Console.WriteLine("Received Add({0},{1})", n1, n2); // Code added to write output to the console window. Console.WriteLine("Return: {0}", result); return result; } public double Subtract(double n1, double n2) { double result = n1 - n2; Console.WriteLine("Received Subtract({0},{1})", n1, n2); Console.WriteLine("Return: {0}", result); return result; } public double Multiply(double n1, double n2) { double result = n1 * n2; Console.WriteLine("Received Multiply({0},{1})", n1, n2); Console.WriteLine("Return: {0}", result); return result; } public double Divide(double n1, double n2) { double result = n1 / n2; Console.WriteLine("Received Divide({0},{1})", n1, n2); Console.WriteLine("Return: {0}", result); return result; } } }
3 承載服務,承載服務有幾種方式,同時又設計到協議的綁定問題,
綁定分類:BasicHttpBinding 最基礎的http綁定,
NetTcpbingding TCP的
NetNamePipeBinding IPC,也就是進程間通信,沒用過。。
WSHttpBinding 這個是特殊的http/Https協議 加了其他東西的
NetMsmqBindiing,這個是消息隊列,我沒用過,這玩意好像需要額外裝微軟的消息隊列才可以用
這裡我基本就用wshttpbinding來實現,
承載方式:1)用控制台程式,直接用servicehost來承載服務,自己構造終結點:
//服務的地址 Uri baseAddress = new Uri("http://localhost:8000/GettingStarted/"); //承載服務的宿主 ServiceHost selfHost = new ServiceHost(typeof(Calculator), baseAddress); try { selfHost.AddServiceEndpoint(typeof(ICalculator), new WSHttpBinding(), "Calculator"); ServiceMetadataBehavior smb = new ServiceMetadataBehavior(); smb.HttpGetEnabled = true; selfHost.Description.Behaviors.Add(smb); selfHost.Open(); Console.WriteLine("The service is ready."); Console.WriteLine("input<exit> to terminate service."); Console.WriteLine(); while ("exit" == Console.ReadLine()) { selfHost.Close(); } } catch (CommunicationException ex) { Console.WriteLine(ex.Message); selfHost.Abort(); Console.ReadLine(); }
2) 用IIS承載,這裡又兩種方式,通過internet的方式 這個可以直接在vs中調試的,選中svc文件點調試會彈出一個wcf的客戶端測試程式,可以直接進行測試,但是這個客戶端不能測試非同步,只能測同步
這中方式主要是需要配置一個web.config文件,部署的話,直接在IIS添加網站服務程式,把生成的DLL和web.config放在一個目錄下應該跟這個調試是一樣的(這種方式IIS5 6 只支持HTTP)
還可以通過WAS(進程激活服務,支持的協議比上邊多,這個就沒有深入研究了···)
<?xml version="1.0" encoding="utf-8"?> <configuration> <appSettings> <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" /> </appSettings> <system.web> <compilation debug="true" targetFramework="4.5.2" /> <httpRuntime targetFramework="4.5.2"/> </system.web> <system.serviceModel> <services> <service name="GettingStartedLib.Calculator" behaviorConfiguration="MyServiceTypeBehaviors" > <host> <baseAddresses> <add baseAddress="http://localhost:8001/"/> </baseAddresses> </host> <endpoint address="CalculatorService" binding="wsHttpBinding" contract="Calculator"> </endpoint> </service> </services> <behaviors> <serviceBehaviors> <behavior name="MyServiceTypeBehaviors" > <!-- 將下列元素添加到服務行為配置中。 --> <serviceMetadata httpGetEnabled="true" /> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> <system.webServer> <!--<modules runAllManagedModulesForAllRequests="true"/>--> <!-- 若要在調試過程中瀏覽 Web 應用程式根目錄,請將下麵的值設置為 True。 在部署之前將該值設置為 False 可避免泄露 Web 應用程式文件夾信息。 --> <directoryBrowse enabled="true"/> </system.webServer> </configuration>
3)通過windows服務程式承載,這個內部起始也是servicehost來實現,只不過是掛在windows服務程式上,需要自己實現一個windows服務,也就是繼承servicebase類,重寫onstart,在onstart中用servicehost載入服務,在stop中停止服務,同時需要配置文件就是程式的配置文件app.config記得生成到目錄下,配置終結點。服務類生成的程式需要安裝,還需要繼承install實現一個安裝類,把生成的服務程式安裝到windows系統服務去,然後就可以在服務中啟動服務,
這個安裝是在管理員許可權下進入對應的.netframework目錄下運行installutil path(程式目錄) 這裡用的是4.5 ,就是v4.0。。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using GettingStartedLib; using System.ServiceModel.Description; using System.ServiceModel; using System.ServiceProcess; using System.Configuration.Install; using System.ComponentModel; namespace WcfConsole { public class serviceCalss : ServiceBase { public ServiceHost serviceHost = null; public serviceCalss() { ServiceName = "WCFWindowsServiceSample"; } protected override void OnStart(string[] args) { if (serviceHost != null) { serviceHost.Close(); } serviceHost = new ServiceHost(typeof(Calculator)); serviceHost.Open(); } protected override void OnStop() { if (serviceHost != null) { serviceHost.Close(); serviceHost = null; } } } class Program { static void Main(string[] args) { ServiceBase.Run(new serviceCalss()); //服務的地址 Uri baseAddress = new Uri("http://localhost:8000/GettingStarted/"); //承載服務的宿主 ServiceHost selfHost = new ServiceHost(typeof(Calculator), baseAddress); //try //{ // selfHost.AddServiceEndpoint(typeof(ICalculator), new WSHttpBinding(), "Calculator"); // ServiceMetadataBehavior smb = new ServiceMetadataBehavior(); // smb.HttpGetEnabled = true; // selfHost.Description.Behaviors.Add(smb); // selfHost.Open(); // Console.WriteLine("The service is ready."); // Console.WriteLine("input<exit> to terminate service."); // Console.WriteLine(); // while ("exit" == Console.ReadLine()) // { // selfHost.Close(); // } //} //catch (CommunicationException ex) //{ // Console.WriteLine(ex.Message); // selfHost.Abort(); // Console.ReadLine(); //} } } [RunInstaller(true)] public class ProjectInstaller : Installer { private ServiceProcessInstaller process; private ServiceInstaller service; public ProjectInstaller() { process = new ServiceProcessInstaller(); process.Account = ServiceAccount.LocalSystem; service = new ServiceInstaller(); service.ServiceName = "WCFWindowsServiceSample"; Installers.Add(process); Installers.Add(service); } } }
對應的配置文件
<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" /> </startup> <system.serviceModel> <services> <!-- This section is optional with the new configuration model introduced in .NET Framework 4. --> <service name="GettingStartedLib.Calculator" behaviorConfiguration="CalculatorServiceBehavior"> <host> <baseAddresses> <add baseAddress="http://localhost:8000/"/> </baseAddresses> </host> <endpoint address="" binding="wsHttpBinding" contract="Calculator" /> </service> </services> <behaviors> <serviceBehaviors> <behavior name="CalculatorServiceBehavior"> <serviceMetadata httpGetEnabled="true"/> <serviceDebug includeExceptionDetailInFaults="False"/> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> </configuration>
4 服務端承載好了,就可以用客戶端調用服務了,又兩種方式
客戶端直接引用已經運行的服務,vs會自動生成一個對應的client類,裡邊又設置好的服務路徑和介面之類的,也可以自己指定路徑,只用他的函數,記得close
// CalculatorClient client = new CalculatorClient(); var client = myChannelFactory.CreateChannel(); double value1 = 100.00D; double value2 = 15.99D; double result = client.Add(value1, value2); Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);
也可以用ChannelFactory<TChannel>類來構造一個client,這種模式是引用服務的契約介面,
var myBinding = new WSHttpBinding(); EndpointAddress myEndpoint = new EndpointAddress("http://localhost:8000/GettingStarted/"); //EndpointAddress myEndpoint = new EndpointAddress("http://localhost:8000/"); ChannelFactory<ICalculator> myChannelFactory = new ChannelFactory<ICalculator>(myBinding, myEndpoint);
這裡邊都可以用配置文件來設置終結點,也就是綁定類型和地址,配置方式與服務端類似
5,6就是配置和調用,這裡就不說了
這裡只是簡單的wcf實例,
實際還有內容沒有涉及:
1 不同的綁定類型,怎麼選擇,網上有個圖是判斷什麼情況選擇對應的綁定類型,其實就是選擇協議
2 加密,因為之間的程式用的是basichttpbingding就是就是基本的http綁定,客戶端沒用引用服務端,服務端以web服務的形式公開服務,客戶端直接以http請求的形式來與服務端通信,這樣客戶端cs和bs可以公用一個服務端,但是沒有加密,傳輸的內容是完全公開的··講道理web服務這玩意本來就是發過去給人看的·公開應該也沒啥好像····,面試的時候問到這個問題直接懵逼了···這裡暫時沒寫 ··這玩意好像挺麻煩的···
3 這裡在4.5的環境下編寫的,服務公開以後會自動生成對應的非同步執行方法,與之前的方式不同,之前是老式的beginxxx的非同步方式,返回一個Iasyncresult的結果,這裡是4.5的async/await的形式來執行非同步操作
4 wcf服務是可以相容asp.net ,也可以返回json格式的對象··這個可以通過設置方法的操作特性可以設置
5 wcf服務支持雙工,這個就很流弊了,不過講道理tcp這種本身就支持雙工,沒具體研究
6 在雙工模式下支持回調,這個意思好像是客戶端請求完成再出發一個回調服務麽··· 沒研究···
。。。。。。