技术开发 频道

建立可绑定的WCF通道



【IT168 专稿】

    本文介绍了如何建立可绑定的WCF通道。首先,我们将研究一下通道的内部机理,然后再看一下如何使用WCF类来实现一个通道。

一、什么是WCF通道

    如果将服务比作人类的身体,那么通道就是我们服务的神经系统。神经系统担负着从身体的各部分接受信息并传递给大脑的重担。其中不同的感觉信息通过多路神经以同样的方式传递给大脑。
    和神经系统类似,通道建立和传输消息,并将这些消息从一个通道传输到另一个通道。但和神经系统不同的是,通道不是使用感觉接受器接受信息,而是通过从网络上接受字节来建立信息。
    有两个通道类型:传输通道(Transport Channels)和协议通道(Protocol Channels)。传输通道负责网络中的通信。而协议通道负责象数据的加密、解密的事情。
    通道栈中处于栈顶的消息被叫做Dispatcher的一系列WCF类获取并发送,Dispatcher将被我们创建并映射到我们的服务的契约(Contract)上。整个通道栈和Dispatcher的安排如图1所示:


              图1

    一个传输通道位于通道栈的最底端。数据(消息)从传输通道开始流动,通过位于通道栈的通道集合。Dispatcher实际上是一系列类的集合。本文并不会详细讨论Dispatcher构造,如果想详细了解Dispatcher的情况,请参阅相关的WCF文档。

    也许到现在我们可以猜到,在通道栈中的通道远比我们想象的多。而正是这些通道的存在使通道栈做着魔术般的工作。


二、什么是通道栈

    一个通道栈由很多通道(Channels)、通道管理器(ChannelManagers)以及其他完成特殊工作的类,如将字节流转换为消息的类。本文将主要关注一下通道和通道管理器所起的角色。
    通道管理器负责建立和维护通道。通道管理器由以下两个部分组成:

1. 通道工厂(ChannelFactories)通过一个客户端通信系统来建立和管理通道。
2. 通道监听器(ChannelListeners)通过一个服务来建立和管理通道。

    本文将关注位于WCF内部的服务,因此,我们我们将关注通道监听器和通道栈中的通道在整个系统中的角色。绑定(Bindings)是一些绑字元素的集合。这些绑定元素建立通道监听器,而监听器建立和管理通道。图2详细描述了这些类的关系。




                                                                 图2
    根据微软提供的文档,数据从一个通道到达另一个通道会衰减,而这些会被通道监听器捕获并处理。
    在我们讨论通道监听器和通道如何在一起工作并为我们提供通讯服务之前,有一个重要的思想必须理解。一个状态机管理WCF通讯中的所有状态。在状态机中的状态都是可预料的,如Open、Abort、Close等等。所有参与通讯状态机的类都支持ICommunicationObject接口。如果想了解完整的WCF通讯状态机的定义,请参阅相关的WCF文档。
    到现在为止,我已经向读者介绍了一个通道栈的组成,下面让我们来看看如何编写代码来控制通道栈。


三、实例概述
    在所有的WCF服务中,有一些服务使用了非常典型的通讯模式。在WCF文档中引用的通讯模式是消息交换模式,但是“Shape”还被描述为我们实现模式的接口类型。下面是三个消息交换模式:

1. 数据报模式,或称为单行模式。在这种模式下数据流仅仅从客户端向服务端传输。
2. 请求响应模式,在这种模式下,数据流从客户端到达服务端,然后服务端又会将响应信息传回客户端。
3. 数据报和请求响应模式,这种模式同时兼有两种模式的特点。

    由于数据报模式是一种基础模式,并且实现起来非常简单。因此,本文的例子将以数据报模式实现。在这个例子中,通道栈中有三个通道。一个是传输通道,负责创建和传输消息,另两个是协议通道,负责将消息头附着在消息数据上。
    管理WCF通讯的状态机可以使用异步或同步的方式调用通道的功能。每一个消息交换模式包含每一个调用类型。为了简单起见,在这里只使用同步方式来实现。还有就是本例直接和通道栈进行交互。因此,在例子代码中并没有声明契约。为了运行例子程序,我们需要最新的SDK。


四、通道的实现

   
从上面的内容可知,本例实现了数据报信息交换模式。因此,通道需要实现IInputChannel接口,代码如下:

class TestTransportChannel : ChannelBase,IInputChannel { protected EndpointAddress localAddress; public TestTransportChannel(TestTransportChannelListener parent, EndpointAddress address) : base(parent) { ... ... this.localAddress = address; }
    在IInputChannel中有两个关键的方法,WaitForMessage和Receive方法。当一个消息出现时,WaitForMessage返回true。然后Receive获得了这个消息。下面的使用其中一个协议栈接收消息的代码:

public Message Receive(TimeSpan timeout) { Message msg; MessageHeader mh; BigHelper.DisplayMessage("Receive " + this.ToString()); msg = ((TestLevel2ChannelListener)this.Manager) .GetMessage(timeout); mh = MessageHeader.CreateHeader ("Level2", "http://Level2ns", "2"); msg.Headers.Add(mh); return msg; }


 Receive方法返回一个Message对象。Message对象将被传递到WCF的各个层。接下来,让我们来看看ChannelListener的实现。



五、通道监听器的实现

    在前面曾经讲过通道和监听器的关系。下面,我们再来回顾一下它们的关系。
    我们可以看到,ChannelListeners是非常有趣的家伙。在本例中,在程序中的每一个通道有一个ChannelListener。在这里我将指出每一个ChannelListener的差异。为了简单起见,我们只看一下协议通道的ChannelListener。
    首先,ChannelListener从ChannelListenerBase类继承。我们可以看到,泛型,作为.NET2.0框架的一个新特性,在WCF中被ChannelListener和其它的类大量的使用。如果这些语法对于读者很陌生,我们可以从这个URL中获得有用的信息:http://msdn2.microsoft.com/en-us/library/ms379564(VS.80).aspx。下面是ChannelListener类的声明:

class TestTransportChannelListener : ChannelListenerBase<IInputChannel> { private Uri _uri; private EndpointAddress _localAddress; public TestTransportChannelListener(TestTransportBindingElement transportElement, BindingContext context) : base(context.Binding) { BigHelper.DisplayMessage("Construct " + this.ToString()); _uri = new Uri(context.ListenUriBaseAddress, context.ListenUriRelativeAddress); _localAddress = transportElement.LocalAddress; }

从上面的代码可以看出,在构造方法的第一个参数负责建立ChannelListener。我们将在后面的部分看到更多的BindingElement的内容。我们可能注意到ChannelListener所存储的通道监听器只存在于通道栈的底层。我们将本文后面的内容学习到更多这方面的知识。
    前面讲过,ChannelListener负责建立通道。在OnAcceptChannel事件发生时接收通道指令。在这个例子中,OnAcceptChannel方法被其中一个协议通道实现,代码如下:
protected override IInputChannel OnAcceptChannel(TimeSpan timeout)
{
   EndpointAddress address = new EndpointAddress(BigHelper.Uri);
 
   _innerChannel = _innerChannelListener.AcceptChannel(timeout);
 
   TestLevel2Channel channel = new TestLevel2Channel(this, address);
 
   return channel;
}

    图3显示了调用AcceptChannel时和系统的交互流产。


                                                                    图3

   
在上面的OnAcceptChannel中可以看出,_innerChannelListener是一个在ChannelStack中比较底层的ChannelListener。这个ChannelListener保存了从当前监听器的底层到上层的通道。这个保存被监听器返回的通道的过程从底至一直延续到上层,然后,应用程序获得了栈顶的一个引用。图4演示了这一过程。由于传输通道是最底层的通道,因此,在代码中,传输通道简单地建立了一个Message对象。


                                                                  图4

    GetProperty
方法允许WCF的其他层使用通道栈。在本文的下面部分将讨论GetProperty方法的使用。


六、将通道绑定在一起


    Binding
由很多BindingElements组成。前面提到过,BindingElements建立ChannelListeners。在这个例子中,每一个BindingElement负责一个特殊的ChannelListener。因此,在BindingElement中大多数的方法是BindChannelListener

public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context) { if (context == null) { throw new ArgumentNullException("context"); } if (!CanBuildChannelListener<TChannel>(context)) { throw new ArgumentException(String.Format("Unsupported channel type: {0}.", typeof(TChannel).Name)); } BigHelper.DisplayMessage("Build Listener " + this.ToString() + " of type " + typeof(TChannel).ToString() ); return (IChannelListener<TChannel>)(object)new TestTransportChannelListener(this, context); }

当我们看下一部分时,会发现WCF开始读取Binding,并从BindingElement集合中建立通道栈。

七、调用通道栈
    本例子中的代码将执行以下动作:
1. 建立一个新的Binding。
2. 从通道栈的栈顶获得ChannelListener。
3. 打开最顶端的ChannelListener。
4. 调用AcceptChannel,并依次调用最顶端的ChannelListener的OnAcceptChannel和返回最顶端的通道。
5. 打开最顶端的通道。
6. 调用最顶端通道的Receive方法,并打印返回的消息。

实现代码如下:

static void Main(string[] args) { TestBinding bind = new TestBinding(); IChannelListener<IInputChannel> listener; IInputChannel channel; Message msg; BindingParameterCollection parms = new BindingParameterCollection(); BigHelper.DisplayComment("Tracing..."); BigHelper.DisplayComment(""); listener = bind.BuildChannelListener<IInputChannel>(parms); listener.Open(); channel = listener.AcceptChannel(); channel.Open(); channel.WaitForMessage(TimeSpan.FromSeconds(3.0)); msg = channel.Receive();

 

 

 



从上面代码可以看出, 我们调用了最顶端的监听器和通道的AcceptChannel,并贯穿于整个通道层,直到传输层通道。

八、总结

    本文的主要目的解释了一些和WCF 通道以及绑定相关的问题。WCF是微软制造的一个非常巨大的企业级系统,主要目的是巩固.NET Framework在通讯领域的地位。很多未来的产品将被建立在它之上。因此,学习WCF对于想提前掌握未来技术的开发人员是非常重要的。
0
相关文章