您的位置:首页 > 理论基础 > 计算机网络

[原创]WCF后续之旅(17):通过tcpTracer进行消息的路由

2008-09-19 15:13 501 查看
对于希望对WCF的消息交换有一个深层次了解的读者来说,tcpTracer绝对是一个不可多得好工具。我们将tcpTracer置于服务和服务代理之间,tcpTracer会帮助我们接获、显示和转发流经他的消息。
从本质上讲,tcpTracer是一个路由器。当启动的时候,我们需要设置两个端口:原端口(source port)和目的端口(destination port),然后tcpTracer就会在原端口进行网络监听。一旦请求抵达,他会截获整个请求的消息,并将整个消息显示到消息面板上。随后,tcpTracer会将该消息原封不动地转发给目的端口。在另一方面,从目的端口发送给原端口的消息,也同样被tcpTracer截获、显示和转发。
接下来我们我们通过下面的步骤演示如何通过tcpTracer在WCF中进行消息的路由。
步骤一、创建一个简单的WCF应用 为了演示tcpTracer在WCF中的应用,我们需要先创建一个简单的WCF服务的应用,为此我们创建一个简单计算服务的例子。
整个应用采用如下图所示的四层结构:Contracts、Services、Hosting和Clients。
Contracts:class library项目,定义所有的契约,包括服务契约、数据契约、消息契约以及错误契约,刚项目同时被其他三个项目引用
Services:class library项目,实现了在Contracts中定义的服务契约
Hosting:控制台项目,同时引用Contracts和Services,实现对定义在Services项目的服务的寄宿
Clients:控制台项目,引用Contracts,模拟服务的调用者



服务契约:Artech.TcpTraceDemo.Contracts.ICalculate
1: //---------------------------------------------------------------
2: // EndpointAddress & WCF Addressing (c) by 2008 Jiang Jin Nan
3: //---------------------------------------------------------------
4: using System.ServiceModel;
5: namespace Artech.TcpTraceDemo.Contracts
6: {
7:     [ServiceContract]
8:     public interface ICalculate
9:     {
10:         [OperationContract]
11:         double Add(double x, double y);
12:     }
13: }


服务实现:Artech.TcpTraceDemo. Services. CalculateService

1: //---------------------------------------------------------------
2: // EndpointAddress & WCF Addressing (c) by 2008 Jiang Jin Nan
3: //---------------------------------------------------------------
4: using Artech.TcpTraceDemo.Contracts;
5: 
6: namespace Artech.TcpTraceDemo.Services
7: {
8:     public class CalculateService:ICalculate
9:     {
10:         #region ICalculate Members
11: 
12:         public double Add(double x, double y)
13:         {
14:             return x + y;
15:         }
16: 
17:         #endregion
18:     }
19: }


服务寄宿(代码):Artech.TcpTraceDemo.Hosting. Program

1: //---------------------------------------------------------------
2: // EndpointAddress & WCF Addressing (c) by 2008 Jiang Jin Nan
3: //---------------------------------------------------------------
4: using System;
5: using System.ServiceModel;
6: using Artech.TcpTraceDemo.Services;
7: 
8: namespace Artech.TcpTraceDemo.Hosting
9: {
10:     class Program
11:     {
12:         static void Main(string[] args)
13:         {
14:             using (ServiceHost serviceHost = new ServiceHost(typeof(CalculateService)))
15:             {
16:                 serviceHost.Opened += delegate
17:                 {
18:                     Console.WriteLine("The Calculate Service has been started up!");
19:                 };
20:                 serviceHost.Open();
21: 
22:                 Console.Read();
23:             }
24:         }
25:     }
26: }
27: 

服务寄宿(配置):App.config

1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3:     <system.serviceModel>
4:         <bindings>
5:             <customBinding>
6:                 <binding name="SimpleBinding">
7:                     <textMessageEncoding />
8:                     <httpTransport />
9:                 </binding>
10:             </customBinding>
11:         </bindings>
12:         <services>
13:             <service name="Artech.TcpTraceDemo.Services.CalculateService">
14:                 <endpoint address="http://127.0.0.1:9999/calculateservice" binding="customBinding"                    bindingConfiguration="SimpleBinding" contract="Artech.TcpTraceDemo.Contracts.ICalculate"/>
15:             </service>
16:         </services>
17:     </system.serviceModel>
18: </configuration>
19: 

注:由于本例仅仅用于模拟消息的路由,所以我们仅仅需要绑定提供的传输和编码功能,所以在这里我使用了自定义绑定,并且添加两个BindElement:HttpTransport和TextMessageEncoding。

服务访问(代码):Artech.TcpTraceDemo.Clients.Program

1: //---------------------------------------------------------------
2: // EndpointAddress & WCF Addressing (c) by 2008 Jiang Jin Nan
3: //---------------------------------------------------------------
4: using System.ServiceModel;
5: using Artech.TcpTraceDemo.Contracts;
6: using System;
7: namespace Artech.TcpTraceDemo.Clients
8: {
9:     class Program
10:     {
11:         static void Main(string[] args)
12:         {
13:             using (ChannelFactory<ICalculate> channelFactory = new ChannelFactory<ICalculate>("calculateService"))
14:             {
15:                 ICalculate calculator = channelFactory.CreateChannel();
16:                 using (calculator as IDisposable)
17:                 { 
18:                     Console.WriteLine("x + y = {2} where x = {0} and y = {1}",1,2,calculator.Add(1,2));
19:                 }
20:             }
21: 
22:             Console.Read();
23:         }
24:     }
25: }
26: 

服务访问(配置):App.config

1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3:     <system.serviceModel>        
4:         <bindings>
5:             <customBinding>
6:                 <binding name="SimpleBinding">
7:                     <textMessageEncoding />
8:                     <httpTransport />
9:                 </binding>
10:             </customBinding>
11:         </bindings>
12:         <client>
13:             <endpoint  address="http://127.0.0.1:9999/calculateservice"
14:                 binding="customBinding" bindingConfiguration="SimpleBinding"
15:                 contract="Artech.TcpTraceDemo.Contracts.ICalculate" name="calculateService" />
16:         </client>
17:     </system.serviceModel>
18: </configuration>
19: 

步骤二、通过ClientViaBehavior实现基于tcpTracer的消息路由

在我们创建的WCF服务来说,整个服务访问只涉及到两方:服务(CalculateService)和服务的调用者(Client)。从消息交换的角度来看,服务的调用者调用者将请求消息直接发送到服务端,计算结果也以回复消息的形式直接返回到服务的调用者。

现在我们需要将tcpTracer作为一个路由器引入到服务(CalculateService)和服务的调用者(Client)之间,那么我们需要解决的是:服务调用者发送的消息不能直接发送到服务端,而应该先发送给tcpTracer,再由tcpTracer转发给服务。我们可以通过ClientViaBehavior实现逻辑地址和物理地址的分离——逻辑地址指向最终的服务,而物理地址则指向tcpTracer。

具体的原理如下图所示:我们将tcpTracer的原端口(source port)和目的端口(destination port)设置成8888和9999(CalculateService地址所在的端口)。通过ClientViaBehavior将物理地址的端口设成8888(tcpTracer监听端口)。





注:对于消息发送方来说,SOAP消息的To报头对应的地址由发送端的终结点地址(逻辑地址)决定。

基于上面的实现原理,我们需要修改客户端的配置,在<system.serviceModel>/<behaviors>/<endpointBehaviors>添加ClientViaBehavior,将viaUri的端口指定为8888:http://127.0.0.1:8888/calculateservice。并将该EndpointBehavior应用到终结点中。

1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3:     <system.serviceModel>
4:         <behaviors>
5:             <endpointBehaviors>
6:                 <behavior name="clientViaBehavior">
7:                     <clientVia viaUri="http://127.0.0.1:8888/calculateservice" />
8:                 </behavior>
9:             </endpointBehaviors>
10:         </behaviors>
11:         <bindings>
12:             <customBinding>
13:                 <binding name="SimpleBinding">
14:                     <textMessageEncoding />
15:                     <httpTransport />
16:                 </binding>
17:             </customBinding>
18:         </bindings>
19:         <client>
20:             <endpoint  address="http://127.0.0.1:9999/calculateservice" behaviorConfiguration="clientViaBehavior"
21:                 binding="customBinding" bindingConfiguration="SimpleBinding"
22:                 contract="Artech.TcpTraceDemo.Contracts.ICalculate" name="calculateService" />
23:         </client>
24:     </system.serviceModel>
25: </configuration>
26: 

现在我们启动tcpTracer,将Listen On Port#和Destination Port #设置为8888和9999。





接下来,我们分别启动服务寄宿和服务访问的控制台应用程序,请求消息和回复消息将会显示到tcpTracer的消息显示面板中,如下图所示:





其中显示在上面文本框中的请求消息的内容如下,可以看出是一个HttpRequest消息,SOAP消息作为HttpRequest消息的主体(body)。

1: POST /calculateservice HTTP/1.1
2: Content-Type: application/soap+xml; charset=utf-8
3: VsDebuggerCausalityData: uIDPo2sY41w6xm1DgtOSzZT5+0EAAAAAXVfsUhiXVUmLsNq6tAEl+rUZZUmtRERFvB6DbqcWQtcACQAA
4: Host: 127.0.0.1:8888
5: Content-Length: 526
6: Expect: 100-continue
7: Connection: Keep-Alive
8: 
9: <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
10:     <s:Header>
11:         <a:Action s:mustUnderstand="1">http://tempuri.org/ICalculate/Add</a:Action>
12:         <a:MessageID>urn:uuid:a63ec626-a350-4390-84c6-fb34be4ff208</a:MessageID>
13:         <a:ReplyTo>
14:                 <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
15:         </a:ReplyTo>
16:         <a:To s:mustUnderstand="1">http://127.0.0.1:9999/calculateservice</a:To>
17:     </s:Header>
18:     <s:Body>
19:         <Add xmlns="http://tempuri.org/">
20:             <x>1</x>
21:             <y>2</y>
22:         </Add>
23:     </s:Body>
24: </s:Envelope>
25: 

相应地,现在在下面文本框中的回复消息是一个HttpResponse消息,主体部分仍然是一个SOAP消息,内容如下:

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

步骤三、通过ListenUri实现基于tcpTracer的消息路由

对于路由的实现,本质上就是实现逻辑地址和物理地址的分离。通过前面的介绍,我们知道了,我们有两种不同的方式实现这样的目标。其中之一我们已经用过了,就是在步骤二种基于ClientViaBehavior的方式,如何说ClientViaBehavior是基于客户端的实现的话,ListenUri就是基于服务端的实现方式。

通过ListenUri的实现的基本原理如下图所示:客户端保持不变,在对服务进行寄宿的时候,将ListenUri的端口设为8888,那么服务实际的监听地址的端口将从9999变成8888。由于客户端保持不变,所以请求消息仍然发送到端口9999,为了实现tcpTracer对消息正常的路由,只需要将原端口和目的端口指定为9999(逻辑地址)和8888(物理地址)就可以了(和步骤二完全相反)。





为此,我们需要修改服务寄宿的配置,在终结点配置节中指定listenUri为http://127.0.0.1:8888/calculateservice

1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3:     <system.serviceModel>
4:         <bindings>
5:             <customBinding>
6:                 <binding name="SimpleBinding">
7:                     <textMessageEncoding />
8:                     <httpTransport />
9:                 </binding>
10:             </customBinding>
11:         </bindings>
12:         <services>
13:             <service name="Artech.TcpTraceDemo.Services.CalculateService">
14:                 <endpoint address="http://127.0.0.1:9999/calculateservice" binding="customBinding"
15:                     bindingConfiguration="SimpleBinding" contract="Artech.TcpTraceDemo.Contracts.ICalculate"
16:                     listenUri="http://127.0.0.1:8888/calculateservice" />
17:             </service>
18:         </services>
19:     </system.serviceModel>
20: </configuration>
21: 

现在我们启动tcpTracer,将Listen On Port#和Destination Port #设置为9999和8888。





当我们先后启动服务寄宿和服务访问的控制台应用程序,在tcpTracer中,我们可以得到和步骤二一样的结果。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐