C# WCF服務(二)

目錄

 

WCF學習

WCF概念

第一個WCF程序

場景

讓WCF程序寄宿

創建客戶端

WCF通信過程

WCF中的 "A","B","C"

Endpoint(終結點)

應用程序間通信

實例

WCF宿主

WCF服務應用程序與WCF服務庫

概述

案例

IIS宿主

Winform應用程序宿主

Winform依賴配置文件

WAS宿主


WCF學習

WCF概念

在基於.NET的應用程序開發中,我們由客戶機的瀏覽器訪問應用程序服務器,然後通過應用程序服務器中的數據庫連接去連接數據庫服務器,讀取或是操作數據,有時候可能會多一個文件服務器。大家可以觀察到,基本上所有的應用都放在了一臺服務器上,但對於一個,由於業務上的需要(如:與外部系統交互),一臺服務器很難支持所有的應用。我們再看下面的圖

客戶機使用瀏覽器訪問服務器A,服務器A爲了業務需要與其他各種應用部署在服務器B、C、D....再通過WCF技術互相通信,相互訪問...然而面向服務的好處不僅僅在此,他還提供了不同語言不同操作系統的可交互性

第一個WCF程序

  1. 新建立空白解決方案,並在解決方案中新建項目,項目類型爲:WCF服務應用程序。建立完成後如下圖所示

  1. 刪除系統生成的兩個文件IService1.cs與Service1.svc。

  2. 添加自定義的WCF【服務文件】CalcService.svc,此時VS2019會自動生成WCF接口文件ICalcService.cs,我們在ICalcService中定義WCF方法Add、Mul、Max、Div,在CalcService.svc.cs對該接口的方法進行實現。

    ICalcService.cs

       [ServiceContract]
        public interface ICalcService
        {
            [OperationContract]。
            double Add(double a,double b);
            [OperationContract]
            double Mul(double a,double b);
            [OperationContract]
            double Max(double a,double b);
            [OperationContract]
            double Div(double a,double b);
        }

    CalcService.svc.cs

      
      public class CalcService : ICalcService
        {
            public double Add(double a, double b)
            {
                return a + b;
            }
    ​
            public double Div(double a, double b)
            {
                return a - b;
            }
    ​
            public double Max(double a, double b)
            {
                return a * b;
            }
    ​
            public double Mul(double a, double b)
            {
                return a / b;
            }
        }

    大家可以看到,在WCF中的接口與普通接口的區別只在於兩個上下文,其他的和我們正常學習的接口一樣。

    定義這個上下文要添加System.ServiceModel的引用。

    [ServiceContract]:來說明接口是一個WCF的接口,如果不加的話,將不能被外部調用。

    [OperationContract]:來說明該方法是一個WCF接口的方法,不加的話同上。

    此時我們的第一個WCF服務程序就建立好了,將CalcService.svc“設爲起始頁”,然後F5運行一下試試,如下圖所示,VS2019自動調用了WCF的客戶端測試工具以便我們測試程序:

場景

在生產中經常應用的場景,把WCF程序寄宿在IIS之上。

IIS:指的是微軟在所有的windows操作系統上提供的一個服務器管理器,能夠將這個windows當做服務器來使用

此時宿主指的就是IIS程序

假設場景如下:A服務器和B服務器。我們把我們剛剛建立的WCF程序“部署”在B服務器上(A,B服務器都放是一臺機器),我們的目標是在A服務器的應用程序來訪問B服務器的WCF程序,實現服務器端的應用程序通訊

讓WCF程序寄宿

  1. 首先我們將WCF應用程序發佈一下,然後部署在B服務器的IIS之上。

  2. 如果不發佈WCF應用程序也可以,但是要保證每次開啓服務要在VS中右鍵在瀏覽器中運行CalcService.svc這個文件。所以比較麻煩。

創建客戶端

我們這裏以Winform應用程序爲例,建立地物理地址爲本機,但是大家可以想像成B服務器是遠程計算機,localhost爲一個其他的IP地址。

新建解決方案,並且創建Winform應用程序的項目。命名爲:WCFClient,如下圖所示:

設計好Winform的UI界面

現在目標調用http://localhost:54544/CalcService.svc?wsdl遠程服務中提供的運算功能

  1. 在客戶端程序的"引用"目錄上“右鍵->添加服務引用”

  2. 在地址中填寫我們的目標服務地址,點“轉到”,就能獲知這個服務現在是否開啓

  3. 命名空間中給服務重新命名,點“確定”

  4. 在計算按鈕的事件中直接調用服務對象,使用計算功能

    在添加的引用的命名空間中有一個類“CalcServiceClient”,實例化這個類,就等於實例化了WCF服務中的類CalcService.svc.cs

           
     private void button1_Click(object sender, EventArgs e)
            {
                double a = double.Parse(textBox1.Text);
                double b = double.Parse(textBox2.Text);
                ServiceCalc.CalcServiceClient client = new ServiceCalc.CalcServiceClient();
                switch (comboBox1.SelectedItem)
                {
                    case "+":
                        label1.Text = client.Add(a,b).ToString();
                        break;
                    case "-":
                        label1.Text = client.Mul(a, b).ToString();
                        break;
                    case "×":
                        label1.Text = client.Max(a, b).ToString();
                        break;
                    case "÷":
                        label1.Text = client.Div(a, b).ToString();
                        break;
                    default:
                        break;
                }
            }

     

WCF通信過程

WCF能夠建立一個跨平臺的安全、可信賴、事務性的解決方案,是一個WebService,.Net Remoting,Enterprise Service,WSE,MSMQ的並集,有一副很經典的對比圖如下:

WCF中的 "A","B","C"

生活中的例子,某一天,公司的領導讓你去送一份合同文件,送文件的過程你可以選擇的交通方式爲“打車”、“公交”、“地鐵”,當然費用是根據發票來報銷的,到了對方公司後你要找到某經理,並且要一份收到合同文件的回執和相關文件。

要完成這項工作任務我們執行以下幾個主要的步驟:

  1. 首先要知道對方公司的地址,引出WCF中的"A"。

    A(Address):英文理解爲"地址",在計算機中是通過一個URL唯一地址標識,通過這個地址我們可以找到我們要調用的WCF服務。

    A解決了:Where to locate the WCF Service?

  2. 選擇我們的交通方式,每種交通方式達到的結果不一樣。如:打車費用較貴、但是過程舒服些,時間上視道路情況而定。公交最便宜,並且可選擇多條線路。地鐵最方便,但是偶爾會很擠,一般都沒座等等,引出WCF中的"B".

    B(Binding):英文理解爲"捆綁,綁定", Binding實現在Client和Service通信的所有底層細節。如:我們在客戶端與服務端傳輸的時候採用的是什麼樣的編碼,XML?Text?二進制?...採用哪種傳輸協議進行傳輸,TCP?Http?以及採用什麼樣的機制解決安全問題,SSL?加密?...

    B解決了:How to communicate with service?

  3. 到了對方公司之後我們能做哪些事?I.送合同,II.拿回執。我們不能要求對方公司給我們其他的東西,引出WCF中的"C"。

    C(Contract):英文理解爲"合同",合同是什麼?告訴我們哪些事能做,哪些事不能做。 Contract的主要的作用是暴露某個WCF Service所提供的所有有效的方法。Contract實際上是把每個方法的轉化成爲相對應的消息。

    C解決了:What functionalities do the Service provide?

Endpoint(終結點)

WCF實現了網絡系統的各個應用程序的通信。各個應用程序的通信是以“終結點(Endpoint)”的來實現的。我們在上面講的實際例子中的A、B、C即是Endpoint 的組成部分,他是服務器間通信調用的入口。

應用程序間通信

我們在第二和第三項中講了A、B、C與Endpoint,現在正式進入應用程序間的通信。我們還是以剛纔送合同的過程爲例

員工A手裏有一張便籤,標記着:地址、綁定、合同.....而合作方手裏也有一張便籤,標記着同樣的內容,並且一直得在等待員工A的出現。只有當便簽上的內容一樣時,合作方A纔會簽署合同回執。

當我們寄宿WCF服務的時候,我們必須定義一個或是多個終結點,然後Serivce端通過監聽這些終結點來處理Client發來的請求。由於應用程序之間是靠Endpoint來通信,那麼我們在Client端也必須定義終結點,只有當Client與Service的終結點完全匹配的時候才能進行通信。

如上圖所示:只有EndpointA中的A、B、C與EndPointB中的A、B、C完全匹配時才能通信。EndPointE與EndpointD也是一樣的。

實例

之前我們主要使用了IIS做爲宿主,客戶端調用WCF服務的是Winform應用程序。今天主要以介紹WCF中傳輸的配置爲主,以體現出"Endpoint"與"A、B、C"。

由於之前的案例沒有手寫任何配置的代碼,Client端的App.config與Service端的Web.config都是自動生成的,當我們添加服務引用時,IDE自動將客戶端的配置文件中Endpoint與引用的服務的Endpoint匹配了。如下代碼所示:

客戶端App.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
    </startup>
    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="BasicHttpBinding_ICalcService" />
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://localhost:54544/CalcService.svc" binding="basicHttpBinding"              bindingConfiguration="BasicHttpBinding_ICalcService" contract="ServiceCalc.ICalcService"
                name="BasicHttpBinding_ICalcService" />
        </client>
    </system.serviceModel>
</configuration>

服務端Web.config代碼:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.6.1" />
    <httpRuntime targetFramework="4.6.1"/>
  </system.web>
    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="BasicHttpBinding_ICalcService" />
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://localhost:54544/CalcService.svc" binding="basicHttpBinding"              bindingConfiguration="BasicHttpBinding_ICalcService" contract="ServiceCalc.ICalcService"
                name="BasicHttpBinding_ICalcService" />
        </client>
    </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <!--若要在調試過程中瀏覽 Web 應用程序根目錄,請將下面的值設置爲 True。在部署之前將該值設置爲 False 可避免泄露 Web 應用程序文件夾信息。-->
    <directoryBrowse enabled="true"/>
  </system.webServer>
</configuration>

 由上面的兩個配置文件我們發現,客戶端system.serviceModel節點有我們剛纔講的endpoint,而服務端爲什麼沒有?這不是和我們剛纔講的有違背嗎?好吧,我們承認IDE生成的Web.config中endpoint很隱晦。那麼我們看下面手工修改後[把對看起來很複雜並且對當前的學習無用的配置節刪掉]的配置文件的代碼(我將服務端和客戶端放在一塊了):

修改之後的服務端Web.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.6.1" />
  </system.web>
  <system.serviceModel>
    <services>
      <service name="WCFService">
        <endpoint address="http://localhost:54544/CalcService.svc" binding="basicHttpBinding"              
         contract="ServiceCalc.ICalcService"
         />
      </service>
    </services>
  </system.serviceModel>
</configuration>

發現WCF服務依然執行成功!這次的配置文件夠明顯了吧,不論是在服務端還是在客戶端,endpoint中的A、B、C都是完全一樣的。也就是終結點完全匹配!

那麼第一次的配置文件爲什麼能執行呢?答案是我們把WCF寄宿在IIS上,而IIS默認監聽的就是Http協議[B確定了]並且地址也是相對於IIS上的文件地址[A確定了],合同更不用說了,找到User.svc什麼都有了[C確定了],所以在服務端就沒有必要顯示的寫出system.serviceModel,不信你試試,把服務端的配置文件中system.serviceModel節刪除,程序一樣可以運行!服務器端的endpoint確定了,客戶端的endpoint自然要和服務端去對應,所以IDE在生成客戶端的配置文件裏endpoint寫的很詳細的,而服務端卻沒有endpoint。

WCF宿主

WCF服務應用程序與WCF服務庫

在平時開發的過程中常用的項目類型有“WCF 服務應用程序”和“WCF服務庫”。

WCF服務應用程序,是一個可以執行的程序,它有獨立的進程,WCF服務類契約的定義,可以直接看到運行的效果。此項目模板基於IIS託管的程序,第一個案例的開發基於IIS託管的WCF服務程序時,比較多見,自學的時候也可以使用這種類型,簡單易懂。

WCF服務庫,可以認爲是一個包含WCF服務以及契約定義的類庫。不能直接運行,你可以在其他項目裏引用,在宿主裏啓用託管這個庫,有點類似於我們在Web項目中應用的類庫。考慮WCF服務設計的時候,服務類的定義爲單獨的庫,可以爲其它項目使用。提高代碼的複用性。

也可以修改這些代碼,比如把WCF服務程序裏的類,移到一個單獨的類庫裏,或是把類庫裏的類移到WCF服務程序中。

概述

通過前面的介紹我們知道,WCF在運行時必寄宿在“宿主程序”之上,WCF本身不能夠獨自運行(每個WCF服務必須宿主在一個Windows進程中)。.Net 提供了多種宿主(控制檯、Winform、WPF、WebForm......)供WCF運行,WCF還是非常靈活的。

WCF的宿主可以是 Windows 服務、COM+應用程序、WAS(Windows Activation Services,Windows進程激活服務)或IIS、Windows應用程序,或簡單的控制檯應用程序及任何.net程序。

案例

重新建立WCF類庫項目爲例做示例,名稱爲:WCFLibrary,並刪除自動生成的兩個文件(IFeed1.cs、Feed1.cs)。如下圖所示:

鼠標右鍵查看項目屬性。我們發現,其實“WCF類庫項目”與我們平時建立的“類庫項目”都是類庫,只不過多了WCF的類庫項目在新建時多了兩個dll的引用(System.ServiceModel.dll、System.Runtime.Serialization.dll)和一個自動生成的配置文件(該配置文件只用於調試時使用,在WCF寄宿以後會應用宿主的配置文件與其他應用程序通信)。這更說明了我們在做分式程序開發的時候與我們平時開發的應用程序沒有多大的區別,只要我們在應用程序間通信時“符合WCF的約定”即可。

服務端我們還和第一個案例一樣(ICalcService接口與CalcService實現),只建立一個方法做爲我們調用的示例代碼如下:

  
  [ServiceContract]
    public interface ICalcService
    {
        [OperationContract]
        double Add(double a,double b);
    }
   
 public class CalcService : ICalcService
    {
        public double Add(double a, double b)
        {
            return a + b;
        }
    }
由於原來的契約爲IFeed,現在
的爲ICalcService,所以配置文件會自動添加一個節點service
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.web>
    <compilation debug="true" />
  </system.web>
  <system.serviceModel>
    <services>
      <service name="WCFLibrary.Feed1">
        <endpoint address="Feed1" behaviorConfiguration="WCFLibrary.Feed1Behavior"
          binding="wsHttpBinding" contract="WCFLibrary.IFeed1" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8733/Design_Time_Addresses/WCFLibrary/" />
          </baseAddresses>
        </host>
   <--自動添加的配置信息
      </service>
      <service name="WCFLibrary.CalcService">
        <endpoint address="" binding="basicHttpBinding" contract="WCFLibrary.ICalcService">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8733/Design_Time_Addresses/WCFLibrary/CalcService/" />
          </baseAddresses>
        </host>
      </service>
     -->
    </services>
    <behaviors>
      <endpointBehaviors>
        <behavior name="WCFLibrary.Feed1Behavior">
          <webHttp />
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

IIS宿主

把WCF寄宿在IIS之上,在IIS中宿主一個服務的主要優點是在發生客戶端請求時宿主進程會被自動啓動,並且你可以依靠IIS來管理宿主進程的生命週期。在開發和使用的過程與Web Service非常相似。

Winform應用程序宿主

建立宿主

  1. 在解決方案下新建Winform項目“WCFIIS”

  2. 添加 System.ServiceModel.dll 的引用

  3. 添加 WCF 服務類庫(WCFLibrary)的項目引用

  4. 創建宿主程序

            public WCFService()
            {
                InitializeComponent();
                toolStripStatusLabel1.Text = "WCF服務未開啓!";
            }
            ServiceHost host = null;
            private void StartBtn_Click(object sender, EventArgs e)
            {  
                //創建宿主的基地址
                Uri uri = new Uri("http://192.168.0.102:4455/CalcService");
                //創建宿主
                using (host=new ServiceHost(typeof(CalcService),uri))
                {
                    //向宿主中添加終結點
                    host.AddServiceEndpoint(typeof(ICalcService),new WSHttpBinding(),"");
                    //將HTTPGetEnable屬性設置爲true
                    ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
                    behavior.HttpGetEnabled = true;
                    //添加行爲到宿主的Behaviors裏面
                    host.Description.Behaviors.Add(behavior);
                    //打開宿主
                    host.Open();
                    toolStripStatusLabel1.Text = "WCF服務啓動成功!";
                }
            }
    ​
            private void CloseBtn_Click(object sender, EventArgs e)
            {
                if (host==null)
                {
                    return;
                }
                host.Close();
                host = null;
                toolStripStatusLabel1.Text = "WCF服務已關閉!";
            }
        }

     

  5. 開啓時必須使用管理特權來運行承載 WCF 服務的進程,如果從 Visual Studio 2019內運行服務,則必須以管理員身份運行 Visual Studio 2019。

    說明宿主建立成功。在上例中用到"ServiceHost"類,這裏只是簡單的應用,具體請查看"MSDN ServiceHost"

建立客戶端

  1. 重新建立解決方案-->Winform應用程序項目。

  2. 添加對服務的引用(在引用上右鍵-->輸入我們定義的服務宿主的基地址(此處爲:http://192.168.0.102:4455/CalcService)-->前往-->確定),命名空間改成“CalcService”

  3. 按照之前的流程編寫客戶端代碼

        public FrmClient()
            {
                InitializeComponent();
            }
    ​
            private void button1_Click(object sender, EventArgs e)
            {
                CalcService.CalcServiceClient client = new CalcService.CalcServiceClient();
                double a = double.Parse(textBox1.Text);
                double b = double.Parse(textBox2.Text);
                label2.Text = client.Add(a,b).ToString();
            }

     

  4. 在這個示例中我們把Endpoint中的ABC,基地址,Behaviors等都直接寫在了代碼裏,但實際應用過程中都是去依賴配置文件,爲了對比說配置明我們下面的例子中會使用配置文件。

Winform依賴配置文件

建立宿主

  1. 在解決方案下新建Winform應用程序項目 WCFIIS。

  2. 添加 System.ServiceModel.dll 的引用。

  3. 添加 WCF 服務類庫(WCFLibrary)的項目引用。

  4. 修改應用程序配置文件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="WCFLibrary.CalcService">
            <host>
              <baseAddresses>
                <add baseAddress="http://192.168.0.102:6789/CalcService"/>
              </baseAddresses>
            </host>
            <endpoint address="" binding="basicHttpBinding" contract="WCFLibrary.ICalcService"></endpoint>
          </service>
        </services>
        <behaviors>
          <serviceBehaviors>
            <behavior>
              <serviceMetadata httpGetEnabled="true"/>
              <serviceDebug includeExceptionDetailInFaults="false"/>
            </behavior>
          </serviceBehaviors>
        </behaviors>
      </system.serviceModel>
    </configuration>

     

  5. 在程序中編寫開啓服務

    private void StartBtn_Click(object sender, EventArgs e)
    {
        host = new ServiceHost(typeof(CalcService));
        host.Open();
        toolStripStatusLabel1.Text = "WCF服務啓動成功!";
    }

     

建立客戶端

同之前的程序一樣,這裏要引用的地址爲:addbaseAddress="http://192.168.0.102:6789/CalcService"/

這個例子中與上一個應用程序不同的是,在Winform應用程序中配置是直接寫在程序中的,而在本例中應用的是配置文件,區別在於如果寫在配置文件中程序運行時直接到配置文件裏取出相關的配置節去創建ServiceHost類。

WAS宿主

IIS7允許通過HTTP外的協議進行激活和網絡通信。此環境適合開發可通過WCF支持的任何網絡協議(包括http、net.tcp、net.pipe、net.msmq)進行通信的WCF服務。部署簡單、管理方便,這些網絡協議在部署時可像Http一樣,直接丟到IIS7上即可,我們在下面的例子中以net.tcp爲協議爲例。IIS7以下的版本只能支持Http的通信。

確保已安裝IIS7的激活組件

在應用WAS宿主時,必須確保IIS7的激活組件安裝好。打開“控制面板”-->“打開或關閉Windows功能”-->“功能”,我的機器上已經安裝過,如下圖所示(WCF激活與非WCF激活)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章