WCF - Binding 深入剖析

WCF的绑定模型

     

      如上图所示,通道和通道栈位于最底层。通道栈构成消息进行通信的通道,组成通道栈的各个通道出于各自的目的对消息进行相应的处理。按照功能划分,可以将通道分成三类:传输通道、消息编码通道和协议通道。其中传输通道和消息编码通道是必不可少的,而协议通道根据不同的需求会不一样。
       WCF中通道栈的创建和生命周期的管理通过通道管理器来进行管理。通道管理器:通道监听器(channel listener)和通道工厂(channel factory)。服务端通过通道监听器对服务请求进行监听,当请求消息被成功检测,则通过通道监听器创建通道栈对请求消息进行接收和处理;客户端通道栈被通道工厂创建,并用于请求消息的处理和发送。

       一般来讲,通道管理器都对应着一个绑定元素对象,绑定元素负责对相应通道管理器的创建。通道管理器负责对通道的创建 ,而绑定元素则负责对通道管理器的创建。从本质上讲,每一个绑定对象,就是一个绑定元素对象的有序集合;绑定元素的构成和次序决定绑定对象的特性。而绑定元素的次序又由绑定上下文(BindingContext)来维护。 

一、通道(Channel)与通道栈 (Channel Stack)

      

      WCF的通信是基于消息的,如果从消息交换的角度讲,通道层则可以看成是进行消息交换参与者之间的中介。通道层通过一个个通道组成一个连续的通道栈,服务端和客户端的通道栈中的通道是对称存在的,该通道栈构成了一个消息流通的管道。消息的发送者通过该管道流到消息的接收者,消息的接收者对消息进行相应的处理。

      WCF完全采用基于消息的通信方式,WCF应用在不同的场景中按照不同的模式进行消息交换。消息交换模式(Message Exchange Pattern:MEP)在SOA中是一个重要的概念。比较典型的消息交换模式包含以下三种:数据报模式(Datagram)、请求/回复模式(Request/Reply)以及双工模式(Duplex)。

      数据报模式(Datagram)
     
数据报模式是最简单的消息交换模式,又称为发送/遗忘(Send/Forget)或者是单向模式(One-way)。数据报模式基于从一个源到一个或者多个目的地的单向消息传输。如下图所示

     

Datagram Service
 1 //------Datagram------
2 //Binding Elements Collection
3 List<BindingElement> dgBingingElements = new List<BindingElement>();
4 dgBingingElements.Add(new OneWayBindingElement());
5 dgBingingElements.Add(new TextMessageEncodingBindingElement());
6 dgBingingElements.Add(new HttpTransportBindingElement());
7
8 //Create Custombinding
9 CustomBinding dgBinding = new CustomBinding(dgBingingElements);
10
11 IChannelListener<IInputChannel> DatagramListener =
12 dgBinding.BuildChannelListener<IInputChannel>(new Uri("http://localhost:8000/Datagram"),
13 new BindingParameterCollection());
14 DatagramListener.Open();
15 Console.WriteLine("Datagram Listener Listening...");
16
17 IInputChannel inputChannel = DatagramListener.AcceptChannel();
18 inputChannel.Open();
19 Message datagramMessage = inputChannel.Receive();
20 Console.WriteLine(string.Format("Receive Message Header : {0}", datagramMessage.Headers));
21 Console.WriteLine(string.Format("Receive Message Body : {0}", datagramMessage.GetBody<string>()));
22
23 datagramMessage.Close();
24 inputChannel.Close();
25 DatagramListener.Close();

Datagram Client
 1 //Datagram
2
3 //Binding Elements Collection
4 List<BindingElement> dgBingingElements = new List<BindingElement>();
5 dgBingingElements.Add(new OneWayBindingElement());
6 dgBingingElements.Add(new TextMessageEncodingBindingElement());
7 dgBingingElements.Add(new HttpTransportBindingElement());
8
9 //Create Custombinding
10 CustomBinding dgBinding = new CustomBinding(dgBingingElements);
11
12 Console.WriteLine("--- Datagram Model---");
13 IChannelFactory<IOutputChannel> outputChannelFactory = dgBinding.BuildChannelFactory<IOutputChannel>(new BindingParameterCollection());
14 outputChannelFactory.Open();
15 Console.WriteLine("Output Channel Factory Opened");
16 IOutputChannel outputChannel =
17 outputChannelFactory.CreateChannel(new EndpointAddress("http://localhost:8000/Datagram"));
18 outputChannel.Open();
19 Console.WriteLine("Output channel opened");
20
21 Message outputMessage = Message.CreateMessage(dgBinding.MessageVersion, "Robert", "Output Message");
22 outputChannel.Send(outputMessage);
23 Console.WriteLine("Output Message Sent");
24
25 outputMessage.Close();
26 outputChannel.Close();
27 outputChannelFactory.Close();


      请求/回复模式(Request/Reply)     
     
请求/回复模式中消息发送方来将消息发送给接收方后会等待对方的回复,如下图所示

     

Request/Reply Service
 1 //Request/Reply
2
3 //Binding Elements Collection
4 List<BindingElement> rrBingingElements = new List<BindingElement>();
5 rrBingingElements.Add(new TextMessageEncodingBindingElement());
6 rrBingingElements.Add(new HttpTransportBindingElement());
7
8 //Create Custombinding
9 CustomBinding rrBinding = new CustomBinding(rrBingingElements);
10
11 IChannelListener<IReplyChannel> RRListener =
12 rrBinding.BuildChannelListener<IReplyChannel>(new Uri("http://localhost:8000/RR"),
13 new BindingParameterCollection());
14 RRListener.Open();
15 Console.WriteLine("Reply Channel Listening...");
16
17 IReplyChannel replyChannel = RRListener.AcceptChannel();
18 replyChannel.Open();
19
20 RequestContext ReplyRequest = replyChannel.ReceiveRequest();
21 Message replyMessage = ReplyRequest.RequestMessage;
22
23 Console.WriteLine(string.Format("Receive Message Header : {0}", replyMessage.Headers));
24 Console.WriteLine(string.Format("Receive Message Body : {0}", replyMessage.GetBody<string>()));
25
26 Message responseMessage = Message.CreateMessage(rrBinding.MessageVersion,
27 "Robert",
28 "Response Message");
29 ReplyRequest.Reply(responseMessage);
30 Console.WriteLine("Responsed");
31 responseMessage.Close();
32 replyMessage.Close();
33 ReplyRequest.Close();
34 replyChannel.Close();

Request/Reply Client
 1 //Request/Reply
2 Console.WriteLine("--- Request/Reply Model---");
3
4 List<BindingElement> rrBingingElements = new List<BindingElement>();
5 rrBingingElements.Add(new TextMessageEncodingBindingElement());
6 rrBingingElements.Add(new HttpTransportBindingElement());
7 //Create Custombinding
8 CustomBinding rrBinding = new CustomBinding(rrBingingElements);
9
10 IChannelFactory<IRequestChannel> requestFactory = rrBinding.BuildChannelFactory<IRequestChannel>(new BindingParameterCollection());
11 requestFactory.Open();
12 Console.WriteLine("RequestChannel Factory Opened");
13 IRequestChannel requestChannel =
14 requestFactory.CreateChannel(new EndpointAddress("http://localhost:8000/RR"));
15 requestChannel.Open();
16 Console.WriteLine("Request channel opened");
17
18 Message requestMessage = Message.CreateMessage(rrBinding.MessageVersion, "Robert", "Request Message");
19
20 Message replyMessage = requestChannel.Request(requestMessage);
21 Console.WriteLine("Request message received.");
22 Console.WriteLine("Reply Message Header {0}", replyMessage.Headers.Action);
23 Console.WriteLine("Reply Message Body:{0}", replyMessage.GetBody<string>());
24
25 replyMessage.Close();
26 requestChannel.Close();
27 requestFactory.Close();


      双工模式(Duplex)
     
双工模式中任何一方都可以向对方发送消息,如下图所示

     

Duplex Service
 1 //Duplex
2
3 //Create NetTcpBinding
4 NetTcpBinding duplexBinding = new NetTcpBinding();
5
6 IChannelListener<IDuplexChannel> duplexChannelListener =
7 duplexBinding.BuildChannelListener<IDuplexChannel>(
8 new Uri("net.tcp://localhost:8000/duplex"), new BindingParameterCollection());
9 duplexChannelListener.Open();
10 Console.WriteLine("Sevices Duplex Channel Listener Opened");
11 IDuplexChannel duplexChannel =
12 duplexChannelListener.AcceptChannel();
13 duplexChannel.Open();
14 Console.WriteLine("Sevices Duplex channel Opened");
15
16 Message duplexInputMessage = duplexChannel.Receive();
17 Console.WriteLine(string.Format("Receive Message Header : {0}", duplexInputMessage.Headers));
18 Console.WriteLine(string.Format("Receive Message Body : {0}", duplexInputMessage.GetBody<string>()));
19
20 Message duplexOutputMessage = Message.CreateMessage(duplexBinding.MessageVersion,
21 "Robert",
22 "Services Duplex Output Message");
23 duplexChannel.Send(duplexOutputMessage);
24 Console.WriteLine("Duplex Output Message has been sent.");
25 duplexOutputMessage.Close();
26 duplexInputMessage.Close();
27 duplexChannel.Close();
28 duplexChannelListener.Close();

Duplex Client
 1 //Duplex
2
3 //Create NetTcpBinding
4 NetTcpBinding duplexBinding = new NetTcpBinding(SecurityMode.Message, true);
5
6 IChannelFactory<IDuplexChannel> duplexChannelFactory =
7 duplexBinding.BuildChannelFactory<IDuplexChannel>(new BindingParameterCollection());
8 duplexChannelFactory.Open();
9 Console.WriteLine("Duplex Channel Factory Opened");
10 IDuplexChannel duplexChannel =
11 duplexChannelFactory.CreateChannel(new EndpointAddress("net.tcp://localhost:8000/duplex"));
12 duplexChannel.Open();
13 Console.WriteLine("Duplex channel opened");
14
15 Message duplexSendMessage = Message.CreateMessage(duplexBinding.MessageVersion, "Robert", "Duplex Message");
16 duplexChannel.Send(duplexSendMessage);
17 Console.WriteLine("Duplex message sent.");
18 Message duplexReceiveMessage = duplexChannel.Receive();
19
20 Console.WriteLine("Duplex message received.");
21 Console.WriteLine("Duplex Message Header {0}", duplexReceiveMessage.Headers.Action);
22 Console.WriteLine("Duplex Message Body:{0}", duplexReceiveMessage.GetBody<string>());
23 duplexReceiveMessage.Close();
24 duplexSendMessage.Close();
25 duplexChannel.Close();
26 duplexChannelFactory.Close();

      通道栈是消息交换的管道,在不同的消息交换模式下,这个管道在消息的发送端和接收端扮演着不同的角色。在数据报模式下,发送端的通道栈的作用是输出(Output)数据报,接收端则是输入(Input)数据报;对于请求恢复模式来说,发送端的作用是发送消息请求(Request),而接收端则是恢复(Reply)请求;而在双工通信模式下,消息交换的双方的地位完全是等价的,它们都具有 输出和输入的功能。

     

      上图接口之间的层次结构:所有的接口均继承自IChannel接口,IDuplexChannel则继承了IOutputChannel和IInput、IChannel两个接口,如下图所示

    
       关于上述接口的详细信息可以到MSDN中查看

二、通道监听器(ChannelLister)&通道工厂(ChannelFactory)

      通道监听器的监听过程:当一个WCF服务被Host之后,Service会一定一个或多个Endpoint,对于每一个不同Endpoint,WCF会通过具体的绑定对象创建一个通道监听器。通道监听器通过调用AcceptChannel创建监听通道栈. 一旦消息请求被成功监听,如果该通道是InputChannel(数据报MEP) 或者DuplexChannel(双工MEP),则调用Receive或者BeginReceive方法接收消息,如果需要向对象发送消息,则通过Send或者BeginSend将消息发给请求者;如果通道是ReplyChannel(请求/回复MEP)则调用ReceiveRequest方法获得一个RequestContext对象,通过该对象获取请求消息并发送回复消息。
      通道监听器相关接口与基类关系图如下,通过继承相关类和接口,我们可以自定义通道监听器。

        
       通道工厂:WCF也为通道工厂定义了两个接口:IChannelFactory和IChannelFactory<TChannel>,两个重载的CreateChannel方法通过目的终结点的地址,以及在手工寻址下不同于目的终结点地址的另一个地址,该地址是消息实际会被发送的地址。
       通道工厂相关接口与基类关系图如下,通过继承相关类和接口,我们可以自定义通道工厂

      

三、绑定元素(BindingElement)
      一个绑定对象对应有一系列绑定元素组成,每个绑定元素负责创建相应的通道。因此绑定元素几何的构成以及它们之间的先后顺序,决定了最终生成的通道栈中的通道组的先后顺序。WCF之所以将绑定和绑定元素分离开发,是基于灵活性,可扩展性考虑的。 由于通道的实际创建者是信道管理器(ChannelFactory和ChannelLister),所以绑定元素只需要实现对通道管理器的创建,从而最终实现对具体通道的创建。所以绑定元素的最根本的功能就是实现对通道监听器和通道工厂的创建。可以通过继承BindingElement自定义绑定元素。

总结、回归绑定

       绑定对象由一系列有序的绑定元素组成,绑定元素最终决定着通道栈中通道的组成,而通道的组成最终又决定了通道栈对消息进行处理方式,所有要确定绑定的特性和能力,可以通过查看其绑定元素的构成来深入了解,同样我们可以通过继承Binding来自定义绑定。
      

posted @ 2011-08-30 19:48  Robert-Fang  阅读(994)  评论(1编辑  收藏  举报