官术网_书友最值得收藏!

2.3.5 案例演示:通過tcpTracer進行消息的路由

本節(jié)一開始介紹了SOA中的服務在不同的情況下?lián)斨煌慕巧悍盏奶峁┱摺⒎盏南M者和中介服務。實際上很多路由工具在本質上都起著中介服務的作用。對于WCF來說,最典型的工具就是tcpTracer。

對于希望對WCF的消息交換有深層次了解的讀者來說,tcpTracer絕對是一個不可多得的好工具。將tcpTracer置于服務和服務代理之間,tcpTracer會幫助我們進行消息的截獲和轉發(fā)。分析截獲的消息有助于我們更加深刻地理解WCF的消息交換機制。

從本質上講,tcpTracer是一個路由器。當它啟動的時候,需要設置兩個端口:原端口(Source port)和目的端口(Destination port),然后tcpTracer就會在原端口進行網(wǎng)絡監(jiān)聽。一旦請求抵達,它會截獲整個請求的消息,并將整個消息顯示到消息面板上。隨后,tcpTracer會將該消息原封不動地轉發(fā)給目的端口。在另一方面,從目的端口發(fā)送給原端口的消息,也同樣被tcpTracer截獲、顯示和轉發(fā)。接下來演示如何通過tcpTracer在WCF中進行消息的路由,步驟如下。

步驟一 創(chuàng)建一個簡單的WCF應用

為了演示tcpTracer在WCF中的應用,須要先創(chuàng)建一個簡單的WCF服務應用,為此我們創(chuàng)建一個簡單計算服務的例子。這個例子將在后續(xù)的章節(jié)中頻繁使用,這里先對該應用的結構作一個詳細介紹,后續(xù)章節(jié)中就不再重復介紹了。

整個應用采用如圖2-10所示的4層結構:Contracts、Services、Hosting和Clients。

圖2-10 消息路由案例程序結構

Contracts:Class library項目,定義所有的契約,包括服務契約、數(shù)據(jù)契約、消息契約及錯誤契約,此項目同時被其他3個項目引用;

Services:Class library項目,實現(xiàn)了在Contracts中定義的服務契約;

Hosting:控制臺(Console)項目,同時引用Contracts和Services,實現(xiàn)對定義在Services項目的服務寄宿;

Clients:控制臺項目,引用Contracts,模擬服務的調用者。

1.服務契約:ICalculator

        using System.ServiceModel;
        namespace Artech.TcpTraceDemo.Contracts
        {
            [ServiceContract(NameSpace=”http://wwww.artech.com/”]
            public interface ICalculator
            {
                [OperationContract]
                double Add(double x, double y);
            }
        }

2.服務實現(xiàn):CalculatorService

        using Artech.TcpTraceDemo.Contracts;
        namespace Artech.TcpTraceDemo.Services
        {
            public class CalculatorService:ICalculator
            {
                public double Add(double x, double y)
                {
                      return x + y;
                }
            }
        }

3.服務寄宿(代碼):Program

        using System;
        using System.ServiceModel;
        using Artech.TcpTraceDemo.Services;
        namespace Artech.TcpTraceDemo.Hosting
        {
            class Program
            {
                static void Main(string[] args)
                {
                      using (ServiceHost serviceHost = new
                        ServiceHost(typeof(CalculatorService)))
                      {
                          serviceHost.Open();
                          Console.Read();
                      }
                }
            }
        }

4.服務寄宿(配置):App.config

        <?xml version="1.0" encoding="utf-8" ?>
        <configuration>
            <system.serviceModel>
                <bindings>
                      <customBinding>
                        <binding name="SimpleBinding">
                            <textMessageEncoding />
                            <httpTransport />
                        </binding>
                      </customBinding>
                </bindings>
                <services>
                      <service name="Artech.TcpTraceDemo.Services.CalculatorService">
                        <endpoint address="http://127.0.0.1:9999/CalculatorService"
                            binding="customBinding" bindingConfiguration=
                            "SimpleBinding" contract="Artech.TcpTraceDemo.Contracts.
                            ICalculator"/>
                      </service>
                </services>
            </system.serviceModel>
        </configuration>

注:由于本例僅僅是模擬消息的路由,只須要綁定提供的傳輸和編碼功能,所以這里使用了自定義綁定,并且添加兩個BindElement:HttpTransport和TextMessageEncoding。

5.客戶端(代碼):Program

        using System.ServiceModel;
        using Artech.TcpTraceDemo.Contracts;
        using System;
        namespace Artech.TcpTraceDemo.Clients
        {
            class Program
            {
                static void Main(string[] args)
                {
                      using (ChannelFactory<ICalculator> channelFactory = new
                        ChannelFactory<ICalculator>("CalculatorService"))
                      {
                        ICalculator calculator = channelFactory.CreateChannel();
                        using (calculator as IDisposable)
                        {
                            Console.WriteLine("x + y = {2} where x = {0} and y =
                              {1}",1,2,calculator.Add(1,2));
                        }
                      }
                      Console.Read();
                }
            }
        }

6.客戶端(配置):App.config

        <?xml version="1.0" encoding="utf-8" ?>
        <configuration>
            <system.serviceModel>
                <bindings>
                      <customBinding>
                        <binding name="SimpleBinding">
                            <textMessageEncoding />
                            <httpTransport />
                        </binding>
                      </customBinding>
                </bindings>
                <client>
                      <endpoint  address="http://127.0.0.1:9999/CalculatorService"
                        binding="customBinding" bindingConfiguration="SimpleBinding"
                        contract="Artech.TcpTraceDemo.Contracts.ICalculator"
                          name="CalculatorService" />
                </client>
            </system.serviceModel>
        </configuration>

步驟二 通過ClientViaBehavior實現(xiàn)基于tcpTracer的消息路由

對于創(chuàng)建的WCF服務來說,整個服務訪問只涉及兩方:服務和服務的調用者。服務的調用者將請求消息直接發(fā)送到服務端,計算結果也以回復消息的形式直接返回給服務的調用者。現(xiàn)在須要將tcpTracer作為一個路由器引入到兩者之間。我們通過ClientViaBehavior這樣一個終結點行為(EndpointBehavior)實現(xiàn)這樣的功能。

具體的原理如圖2-11所示:將tcpTracer的原端口(Source port)和目的端口(Destination port)設置成8888和9999(CalculatorService地址所在的端口)。若通過ClientViaBehavior端口設成8888(tcpTracer監(jiān)聽端口),那么客戶端會將消息發(fā)送給tcpTracer監(jiān)聽端口,消息就會被其截獲。最終tcpTracer將截獲的消息轉發(fā)到服務真正的目的地址。服務的回復消息也會沿著相同的路由返回。

圖2-11 基于ClientViaBehavior的消息路由

注:對于消息發(fā)送方來說,SOAP消息的To報頭對應的地址由發(fā)送端的終結點地址(邏輯地址)決定。

基于上面的實現(xiàn)原理,須要修改客戶端的配置,在<system.serviceModel>/<behaviors>/<endpointBehaviors>添加ClientViaBehavior,將viaUri的端口指定為8888:http://127.0.0.1:8888/CalculatorService。并將該EndpointBehavior應用到終結點中。

        <?xml version="1.0" encoding="utf-8" ?>
        <configuration>
            <system.serviceModel>
                <behaviors>
                      <endpointBehaviors>
                        <behavior name="clientViaBehavior">
                            <clientVia viaUri=
                              "http://127.0.0.1:8888/CalculatorService" />
                        </behavior>
                      </endpointBehaviors>
                </behaviors>
                <bindings>
                      <customBinding>
                        <binding name="SimpleBinding">
                            <textMessageEncoding />
                            <httpTransport />
                        </binding>
                      </customBinding>
                </bindings>
                <client>
                      <endpoint  address="http://127.0.0.1:9999/CalculatorService"
                      behaviorConfiguration="clientViaBehavior"
                        binding="customBinding" bindingConfiguration="SimpleBinding"
                        contract="Artech.TcpTraceDemo.Contracts.ICalculator"
                          name="CalculatorService" />
                </client>
            </system.serviceModel>
        </configuration>

現(xiàn)在啟動tcpTracer,將Listen On Port#和Destination Port #設置為8888和9999,如圖2-12所示。

圖2-12 tcpTracer的端口設置

接下來,分別啟動服務寄宿和服務訪問的控制臺應用程序,請求消息和回復消息將會顯示到tcpTracer的消息顯示面板中,如圖2-13所示。

圖2-13 tcpTracer截獲的消息

其中顯示在上面文本框中的請求消息的內(nèi)容如下,可以看出它是一個HttpRequest消息,以SOAP消息作為HttpRequest消息的主體(body)。

        POST /CalculatorService HTTP/1.1
        Content-Type: application/soap+xml; charset=utf-8
        VsDebuggerCausalityData: uIDPo2sY41w6xm1DgtOSzZT5+0EAAAAAXVfsUhiXVUmLsNq-
          6tAEl+rUZZUmtRERFvB6DbqcWQtcACQAA
        Host: 127.0.0.1:8888
        Content-Length: 526
        Expect: 100-continue
        Connection: Keep-Alive
        <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
          xmlns:a="http://www.w3.org/2005/08/addressing">
            <s:Header>
                <a:Action s:mustUnderstand="1">http://tempuri.org/ICalculator/
                  Add</a:Action>
                <a:MessageID>urn:uuid:a63ec626-a350-4390-84c6-fb34be4ff208
                  </a:MessageID>
                <a:ReplyTo> <a:Address>http://www.w3.org/2005/08/addressing/
                  anonymous</a:Address>
                </a:ReplyTo>
                <a:To s:mustUnderstand="1">http://127.0.0.1:9999/
                  CalculatorService</a:To>
            </s:Header>
            <s:Body>
                <Add xmlns="http://tempuri.org/">
                      <x>1</x>
                      <y>2</y>
                </Add>
            </s:Body>
        </s:Envelope>

相應地,在下面文本框中的回復消息是一個HttpResponse消息,主體部分仍然是一個SOAP消息,內(nèi)容如下。

        HTTP/1.1 100 Continue
        HTTP/1.1 200 OK
        Content-Length: 394
        Content-Type: application/soap+xml; charset=utf-8
        Server: Microsoft-HTTPAPI/2.0
        Date: Sat, 13 Sep 2008 17:29:37 GMT
        <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
          xmlns:a="http://www.w3.org/2005/08/addressing">
            <s:Header>
                <a:Action s:mustUnderstand="1">http://tempuri.org/ICalculator/
                  AddResponse</a:Action>
                <a:RelatesTo>urn:uuid:a63ec626-a350-4390-84c6-fb34be4ff208
                  </a:RelatesTo>
            </s:Header>
            <s:Body>
                <AddResponse xmlns="http://tempuri.org/">
                      <AddResult>3</AddResult>
                </AddResponse>
            </s:Body>
        </s:Envelope>

步驟三 通過ListenUri實現(xiàn)基于tcpTracer的消息路由

對于路由的實現(xiàn),本質上就是實現(xiàn)邏輯地址和物理地址的分離。ClientViaBehavior實際上是通過在客戶端分離邏輯地址(服務的目的地址)和物理地址(消息被發(fā)送的目的地址)實現(xiàn)了消息路由。接下來模擬的是在服務端分離邏輯地址與物理地址(監(jiān)聽地址)的解決方案。

通過ListenUri實現(xiàn)的基本原理如圖2-14所示:客戶端保持不變,在對服務進行寄宿的時候,將ListenUri的端口設為8888,那么服務實際的監(jiān)聽地址的端口將從9999變成8888。由于客戶端保持不變,所以請求消息仍然發(fā)送到端口9999,為了實現(xiàn)tcpTracer對消息正常的路由,只需要將原端口和目的端口指定為9999(邏輯地址)和8888(物理地址)就可以了(和步驟二完全相反)。

圖2-14 基于ListenUri的消息路由

為此,我們需要修改服務寄宿的配置,在終結點配置節(jié)中指定listenUri為http://127.0.0.1:8888/CalculatorService

        <?xml version="1.0" encoding="utf-8" ?>
        <configuration>
            <system.serviceModel>
                <bindings>
                      <customBinding>
                        <binding name="SimpleBinding">
                            <textMessageEncoding />
                            <httpTransport />
                        </binding>
                      </customBinding>
                </bindings>
                <services>
                      <service name="Artech.TcpTraceDemo.Services.CalculatorService">
                        <endpoint address="http://127.0.0.1:9999/CalculatorService"
                          binding="customBinding"
                            bindingConfiguration="SimpleBinding" contract="Artech.
                            TcpTraceDemo.Contracts.ICalculator"
                            listenUri="http://127.0.0.1:8888/CalculatorService" />
                      </service>
                </services>
            </system.serviceModel>
        </configuration>

現(xiàn)在我們啟動tcpTracer,將Listen On Port#和Destination Port #設置為9999和8888。

圖2-15 tcpTracer的端口設置

當我們先后啟動服務寄宿和服務訪問的控制臺應用程序,在tcpTracer中,我們可以得到和步驟二一樣的結果。

主站蜘蛛池模板: 沈丘县| 明溪县| 和龙市| 乐清市| 博罗县| 蓬莱市| 三穗县| 岐山县| 彩票| 广河县| 巧家县| 哈巴河县| 阿拉善左旗| 亚东县| 类乌齐县| 南郑县| 长沙县| 广南县| 陇南市| 石棉县| 新兴县| 南康市| 德清县| 兴和县| 三都| 长垣县| 武穴市| 仪征市| 西乌| 洱源县| 灌阳县| 深水埗区| 锦屏县| 霸州市| 株洲市| 伊春市| 定兴县| 临泉县| 抚顺县| 府谷县| 门头沟区|