SilverLight通過Net.TCP(NetTCPBinding)方式調用WCF服務


在SilverLight中通過標准的BasicHttpBinding來調用WCF服務是非常容易的, 只要通過VS的添加服務引用功能添加一下就直接能用了, 但是通過net.tcp綁定來調用則相當麻煩. 

 

一. 創建解決方案

首先在VS中創建一個新的SilverLight項目, 將項目命名為SilverLightTcpBindingSample, 在隨后彈出的對話框中選擇創建一個新的WebApplication作為SilverLight的宿主網站, 該網站的名字被自動命名為SilverLightTcpBindingSample.Web.

解決方案的結構為:

SilverLightTcpBindingSample(Solution)

    --SilverLightTcpBindingSample(SilverLight Project)

    --SilverLightTcpBindingSample.Web

 

二. 服務端的配置和部署

在SilverLightTcpBindingSample.Web項目中右擊->Add->New Item->WCF Service, 保持默認的名字Service1, 項目中就會添加IService.cs和Service1.svc 兩個文件.

在IService.cs中會自動生成一個名為IService1的接口 和一個默認的DoWork函數, 將它的定義稍做修改:

    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        string DoWork(string text);
    }

 

然后在Service1.svc中為這個函數添加點內容:

    public class Service1 : IService1
    {
        public string DoWork(string text)
        {
            return string.Format("{0}, {1}", DateTime.Now.ToString("HH:mm:ss") , text.Length);
        }
    }

 

功能上就算完工了, 然后打開web.config, <system.serviceModel>節應該已經存在了, 如果不存在, 手工添加之. 

按下述代碼定義<system.serviceModel>節:

  <system.serviceModel>
 
    <bindings>
      <netTcpBinding>
        <binding name="tcpBindingConfig1">
          <security mode="None"></security>
        </binding>
      </netTcpBinding>
    </bindings>
 
    <services>
      <service name="SilverLightTCPBindingSample.Web.Service1">
        <endpoint address=""
                  binding="netTcpBinding"
                  bindingConfiguration="tcpBindingConfig1"
                  contract="SilverLightTCPBindingSample.Web.IService1">
        </endpoint>
        <endpoint address="mex"
                  binding="mexTcpBinding"
                  contract="IMetadataExchange">
        </endpoint>
        <host>
          <baseAddresses>
            <add baseAddress="net.tcp://localhost:4502/SilverLightTCPBindingSample.Web/Service1.svc"/>
          </baseAddresses>
        </host>
      </service>
    </services>
 
    <behaviors>
      <serviceBehaviors>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"
        multipleSiteBindingsEnabled="true" />
  </system.serviceModel>

 

這里為不熟悉配置文件的同學稍微解釋兩句, 熟悉的請直接跳過這幾段:

<bindings>節實際上是起configuration的作用, 它與任何的服務都不相關, 它定義具體某種binding的細節配置, 例如這里將tcpBinding的securityMode配置為None.

*需要注意的是: SilverLight不支持WCF的Security模型, 所以如果想在SL中調用此服務, 就必須把Security設置為None. 缺省模式下, SecurityMode是Transport, 所以這一節不可省略, 必須顯式配置.

 

關於服務的信息都配置在Services節中. 這里有兩個endpoint, 一個是供客戶端調用的, 另一個是公布元數據的, 用於服務信息的生成. 在host節中添加的baseAddress需要注意, 4502這個端口號不是我瞎編的, 而是SilverLight只能使用4502至4534之間的端口( 我怎么知道的? 在SilverLight的異常中就有非常完整的提示, 這里節錄一句: You may need to contact the owner of the service to expose a sockets cross-domain policy over HTTP and host the service in the allowed sockets port range 4502-4534) , 所以這里就使用了4502端口.

下面的Behaviors節和serviceHostingEnvironment 節是自動生成的, 不用管它.

好了!

現在直接就把SilverLightTcpBindingSample.Web項目發布到某個文件夾下(這里發布到D:\Websites\SilverLightTcpBindingSample.Web). 然后開始IIS的配置. (因為只有IIS才支持net.tcp綁定, 所以必須發布到iis才能進行后續的測試)

2.1 檢查WCF Activation

WCF Activation是Windows的一個可選組件, 默認情況下並沒有安裝, 只有先裝上它, IIS才能支持非HTTP管道的WCF調用.

在控制面板->程序->打開或關閉Windows功能中, 找到.Net framework 3.5 ( 或4.5, 如下圖)

image

這里因為我的操作系統是win8, 所以同時存在.net 3.5和.net 4.5兩項, 如果是win7及以下的操作系統, 應該只有.net framework 3.5, 將.net 3.5項展開, 勾選下面的WCF Http Activation和WCF Non-HTTP Activation.

*需要注意的是: 當確認並安裝完成上述兩項以后, IIS會變異常, 運行任意一個IIS下的項目都會報如下錯誤:

未能從程序集"System.ServiceModel,Version=3.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089"中加載類型"System.ServiceModel.Activation.HttpModule"

這時需要打開cmd, 定位到C:\Windows\Microsoft.NET\Framework\v4.0.30319目錄下, 執行命令:  aspnet_regiis –i, 重新安裝iis才能修復.

因此如果需要在正式的web服務器上安裝WCF Activation, 需要注意這個問題.

 

2.2 編輯默認網站的net.tcp綁定端口

本來這是一件很簡單的事情, 在IIS中找到默認網站節點, 右擊->Edit Bindings:

image

 

在彈出的窗口中找到net.tcp, 點擊edit(默認是808, 這里我已經改成了4502) :

image

然后在彈出的編輯框中把808:* 改成4502:* 即可:

image

但是, 我的win8系統無論是點Add, 還是編輯, 一點保存一定報錯, 錯誤信息是: Object reference not set to an instance of an object, 對象空引用…

image

 

難以相信微軟老大會犯這么低級的錯誤, 但是他們就是這么干了, 咱能怎么着? 我在網上查了一下, 也有其他人說遇到了跟我同樣的問題, 但是微軟並沒有正面回復. 我不知道win7下有沒有問題, 但是windows server 2008下確定是沒有問題.

總之, 如果你跟我一樣遇到了這個令人蛋疼的問題, 就得換一種方式來修改端口號:

先從上面的編輯窗口中把net.tcp刪了(刪除是不會報錯的..) ,

然后打開cmd, 定位到C:\Windows\System32\inetsrv 目錄下, 執行下述命令:

appcmd set site /site.name:"Default Web Site" /+bindings.[protocol='net.tcp',bindingInformation='4502:*']

執行完了以后回到剛才的編輯binding窗口, 會發現net.tcp已經綁定到4502端口了.

然后在默認網站中添加Application, 把剛才發布的項目添加進IIS.

在項目上右擊->Manage Applicatoin->Advanced Settings:

image

在彈出的窗口中最下面一行, Enabled Protocols欄, 原來只有一個http, 在后面加一個逗號, 寫上net.tcp.

image

 

2.3 允許跨域訪問

SilverLight有一個很令人厭惡的設定就是跨域訪問限制, 如果要跨域訪問, 必須在服務網站上放一個policy文件… 不說廢話了, 直接上代碼:

<?xml version="1.0" encoding ="utf-8"?>
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from>
        <domain uri="*"/>
      </allow-from>
      <grant-to>
        <socket-resource port="4502-4506" protocol="tcp" />
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>

在IIS中定位到默認網站節點, 右擊, 選explore, 即可打開網站所在的目錄:

image

如果你沒有修改過它, 這個路徑應該是 C:\inetpub\wwwroot,  在這個文件下新一個文本文件, 將其命名為clientaccesspolicy.xml, 注意!  這個文件名不能弄錯, 必須叫這個名字, 要連同原文本文件的擴展名一起改掉( 什么? 你的電腦上沒有顯示擴展名? …你真的是一個程序員嗎? ) , 然后隨便用什么文本編輯器打開這個文件, 把上面那一段貼進去, 保存.

2.4 測試服務

服務端到這里就差不多算是大功告成了, 從IIS中瀏覽一下Service1.svc吧, 你應該會看到如下的界面:

image

 

當然, 其實這個界面還是有問題的(問題無處不在啊…) , 由於我們在配置服務的時候使用的是tcp元數據endpoint, 所以實際上很多人說在上面這個界面中, 自動生成的提示應該類似於這樣:

svcutil.exe net.tcp://localhost:4502/……

而不是像我的電腦這樣, 還是http打頭的地址.

事實上, 同樣的步驟, 我在windows server 2008上確實看到IIS自動生成的界面上寫着net.tcp://打頭的地址, 可惜, 那是我很久以后才看到的, 一開始我在我自己電腦上測試的時候, 為此困惑了很久, 不明白為什么它要生成http://打頭的地址, 可能又是萬惡的win8吧…(我到底為什么要裝win8?)

any way, 如果你跟我一樣看到的是http://打頭的地址, 也沒關系, 實際上還是可以通過tcp地址生成服務引用的.

打開developer command promp for vs2012, (在開始菜單->vs工具正面), 先輸入d:, 回車, 切換到d盤根目錄, 然后輸入下述命令:

svcutil net.tcp://localhost:4502/SilverLightTCPBindingSample.Web/Service1.svc/mex

它應該會在d盤根目錄下生成service1.cs和output.config兩個文件:

image

當然, 實際上這一步驟並不是必需, 這仍然是為了做測試, 以確保服務確實被正確配置了.

如果到這一步都沒有出現問題, 那就說明服務器端的配置和部署, 是真的接近於完成了… ( 如果僅在localhost上測試, 就已經完成了, 如果這是正式的web server, 很可能還需要在防火牆中開放4502端口, 所以說是接近於完成)

 

三. SilverLight端的配置

3.1 引用服務

這里是個有點歧義的地方, 我見有人說這里可以直接引用net.tcp://打頭的地址, 但是我測試是不可以的, 用的是瀏覽器中的http地址: http://localhost/SilverLightTCPBindingSample.Web/Service1.svc.

引用完成之后, 會自動生成一個ServiceReference.ClientConfig文件, 該文件的內容如下:

<configuration>
    <system.serviceModel>
        <bindings>
            <customBinding>
                <binding name="NetTcpBinding_IService1">
                    <binaryMessageEncoding />
                    <tcpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" />
                </binding>
            </customBinding>
        </bindings>
        <client>
            <endpoint address="net.tcp://localhost:4502/SilverLightTCPBindingSample.Web/Service1.svc"
                binding="customBinding" bindingConfiguration="NetTcpBinding_IService1"
                contract="ServiceReference1.IService1" name="NetTcpBinding_IService1" />
        </client>
    </system.serviceModel>
</configuration>

 

這里需要注意的是: 如果嚴格按照前面我所說的順序操作, 這里會自動生成這個配置文件, 但是我一開始做的時候, 服務端的元數據endpoint用的不是tcp而是namedPipes, 生成的配置文件是空的, 我困惑了很久無解, 后來無奈在這里手動敲入的配置. 最后才發現原來是元數據endpoint的問題.

 

3.2 編寫測試頁面

非常簡單地, 在MainWindow.xaml中放兩個控件:

    <Grid>
        <StackPanel>
            <TextBox x:Name="txt1"></TextBox>
            <Button x:Name="btn1" Content="Click me" Click="btn1_Click"></Button>
        </StackPanel>
    </Grid>

在cs文件中:

        private void btn1_Click(object sender, RoutedEventArgs e)
        {
            var proxy = new ServiceReference1.Service1Client();
            proxy.DoWorkCompleted += proxy_DoWorkCompleted;
            proxy.DoWorkAsync(txt1.Text);
        }

        void proxy_DoWorkCompleted(object sender, ServiceReference1.DoWorkCompletedEventArgs e)
        {
            txt1.Text = e.Result;
        }

編譯整個項目, 運行SilverLightTcpBindingSample.Web項目中的SilverLightTCPBindingSampleTestPage.aspx, 會看到一個文本框和一個按鈕 , 在文本框中輸入一個a, 點擊按鈕, 應該會看到類似下面的界面:

image

前面是一個時間, 后面是字符串長度.

 

結語:

Tcp相對於basic http來說的性能優勢顯而易見, 所以為此麻煩一些是完全值得的.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM