最近項目中,需要對外部程序提供服務接口,用來進行數據交互和部分設備控制。由於都是使用的.NET平台開發的,因此想到使用WCF服務。之前也用過WCF服務,但是當初使用的時候是通過IIS寄宿的,有些地方不太讓人滿意,一則是同一個軟件要部署兩個地方,CS的桌面程序和寄宿於IIS的WCF服務部分,二則是由於系統本身問題,使用的是SQLite數據庫,無法實現多線程訪問,造成了數據重復,容易造成數據不一致。所以現在准備把WCF服務寄宿於CS程序中,這樣就解決了以上兩個問題。
由於對WCF一知半解,只知道按部就班的使用,從明白過其中的道理,所以在網上找了些教程[1],實現了自己的想法,但其中碰到不少問題,想來還是記下,或許以后還有用處。
本文使用一個最簡單的控制台應用程序,同時通過使用WcfTestClient.exe測試,來完成這個示例。
一、在代碼中實現並配置WCF服務。
在VS2012中創建一個控制台應用程序項目CsWcf,並添加接口ICalculator作為契約接口、類CalculatorService作為服務實現此接口中的操作方法,代碼如下:
1. ICalculator.cs--服務契約
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using System.ServiceModel;
6:
7: namespace CsWcf
8: {
9: [ServiceContract]
10: public interface ICalculator
11: {
12: [OperationContract]
13: double Add(double x, double y);
14: }
15: }
2. CalculatorService.cs--服務實現
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using System.ServiceModel;
6:
7: namespace CsWcf
8: {
9: public class CalculatorService:ICalculator
10: {
11: public double Add(double x, double y)
12: {
13: return x + y;
14: }
15: }
16: }
3. 在Program.cs中的Main函數中,添加如下代碼,實現WCF服務的寄宿,並開啟服務。
1: using (ServiceHost host = new ServiceHost(typeof(CalculatorService)))
2: {
3: host.AddServiceEndpoint(typeof(ICalculator), new WSHttpBinding(), "http://192.168.1.102:10002/CalculatorService");
4:
5: if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)
6: {
7: ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
8: behavior.HttpGetEnabled = true;
9: behavior.HttpGetUrl = new Uri("http://192.168.1.102:10002/CalculatorService");
10: host.Description.Behaviors.Add(behavior);
11: }
12:
13: host.Opened += delegate
14: {
15: Console.WriteLine("CalculaorService已經啟動,按任意鍵終止服務!");
16: };
17:
18: host.Open();
19: Console.Read();
20: }
4. F5調試執行,服務開啟后,打開WcfTestClient,添加服務地址,可以正確調用Add服務函數。
但是,以上將服務相關配置寫到代碼中了,這不利於后面的安裝和部署。所以需要將配置信息放置在config配置文件中。
二,使用配置文件動態配置WCF服務。
配置app.config文件,實現WCF服務的可配置性。在項目中添加應用程序配置文件,並添加以下內容:
1: <system.serviceModel>
2: <behaviors>
3: <serviceBehaviors>
4: <behavior name="metadataBehavior">
5: <serviceMetadata httpGetEnabled="true" httpGetUrl="http://192.168.1.102:10002/CalculatorService/metadata" />
6: </behavior>
7: </serviceBehaviors>
8: </behaviors>
9: <services>
10: <service behaviorConfiguration="metadataBehavior" name="CsWcf.CalculatorService">
11: <endpoint address="http://192.168.1.102:10002/calculatorservice" binding="wsHttpBinding"
12: contract="CsWcf.ICalculator" />
13: <endpoint address="http://192.168.1.102:10002/calculatorservice/MEX/" binding="mexHttpBinding"
14: contract="IMetadataExchange" />
15: </service>
16: </services>
17: </system.serviceModel>
同時需要刪除Program.cs中的配置代碼:
1: using (ServiceHost host = new ServiceHost(typeof(CalculatorService)))
2: {
3: host.Opened += delegate
4: {
5: Console.WriteLine("CalculaorService已經啟動,按任意鍵終止服務!");
6: };
7:
8: host.Open();
9: Console.Read();
10: }
F5調試執行,與剛剛的執行結果一樣!此處要特別注意:配置節中的第二個endpoint,即MEX配置,一定要加上,否則會出現如下錯誤:
錯誤: 無法從 http://192.168.1.102:10002/CalculatorService 獲取元數據如果是您有權訪問的 Windows (R) Communication Foundation 服務,請檢查是否已啟用在指定地址發布元數據。有關啟用元數據發布的幫助,請參閱 http://go.microsoft.com/fwlink/?LinkId=65455 上的 MSDN 文檔。WS-Metadata Exchange 錯誤 URI: http://192.168.1.102:10002/CalculatorService 元數據包含無法解析的引用:“http://192.168.1.102:10002/CalculatorService”。 Sendera:BadContextToken無法處理消息。這很可能是因為操作“http://schemas.xmlsoap.org/ws/2004/09/transfer/Get”不正確,或因為消息包含無效或過期的安全上下文令牌,或因為綁定之間出現不匹配。如果由於未處於活動狀態導致服務中止了該通道,則安全上下文令牌無效。若要防止服務永久中止閑置會話,請增加服務終結點綁定上的接收超時。HTTP GET Error URI: http://192.168.1.102:10002/CalculatorService 下載“http://192.168.1.102:10002/CalculatorService”時出錯。 請求因 HTTP 狀態 400 失敗: Bad Request。
我在Artech的文章[1]中學習中並沒有看到第二個終結點的配置,所以運行的時候一直出錯,找了好久才找到這個解決辦法,可以參考“WCF Part 4 : Make your service visible through metadata”這篇文章[2]。
三、其它機器訪問權限限制的解決辦法
以上兩步驟,我們完成了WCF服務的寄宿、發布和訪問,但是如果在其它計算機上訪問此WCF服務時,會發現權限提示:
無法滿足對安全令牌的請求,因為身份驗證失敗!
這個時候,默認的安全是計算機名和用戶名做為權限要求。如果想讓其他用戶也可以隨時訪問到些WCF服務,就需要修改配置文件中的安全級別,設置<security>的mode為None就可以了。同時更改一個endpoint的bindingConfiguration="wsHttpBindingConfiguration"。
1: <bindings>
2: <wsHttpBinding>
3: <binding name="wsHttpBindingConfiguration" maxReceivedMessageSize="20971510">
4: <readerQuotas maxStringContentLength="20971520" maxArrayLength="20971520"/>
5: <security mode="None"/>
6: </binding>
7: </wsHttpBinding>
8: </bindings>
再次運行此服務,其它計算機上也可以訪問此服務了。
總結:WCF用起來簡單,“制作”起來貌似也很簡單,和寫其它的代碼基本無異,但是真正使用起來的時候,卻又經常碰到這樣那樣的問題,個人感覺WCF是一個極其精密的儀器,操作簡單,但是儀器的操作步驟和順序卻比較麻煩,而且零部件不牢固,經常脫落損壞,所以要想更好的使用它,需要對它有個很好的了解才是。由於現在網絡的存在,好多東西都是用的時候搜索一下,基本沒有系統、深入的學習過,這對以后的學習和工作經驗的積累都用處不太大。
[1] Artech, 我的WCF之旅(1):創建一個簡單的WCF程序
[2] Dennis van der Stelt, WCF Part 4 : Make your service visible through metadata