《WCF技术内幕》翻译34:第2部分_第6章_通道:通道接口和基本类型

本节目录:

通道接口和基本类型

IChannel接口

数据报通道:IInputChannelIOutputChannel

发送接口:IOutputChannel

接收接口:IInputChannel

请求/应答通道:IRequestChannelIReplyChannel

发送接口:IRequestChannel

接收接口:IReplyChannel

请求/应答关联:RequestContext类型

双工通道:IDuplexChannel

发送和接收接口:IDuplexChannel

IDefaultCommunicationTimeouts接口

ChannelBase类型

 

通道接口和基本类型

本章开始部分曾经提到过,学习WCF通道基础结构的一个关键部分就是了解WCF系统在通道层使用的接口和类型。本节系统整理了这些复杂的类型系统,深入浅出地讲述各个接口和类型的原理,使得读者可以更容易掌握这些知识点。

IChannel接口

System.ServiceModel.Channels.IChannel接口看似简单,但是它对于通道层的实现至关重要。所有的通道和通道工厂必须实现它。换句话说,一个集成了CommunicationObject的类型通常也会实现IChannel接口。在详细学习IChannel接口的作用以前,我们先来看看它的基本结构:

public interface IChannel : ICommunicationObject {
      T GetProperty<T>() where T: class;
}

你或许会问自己:“为什么会这么重要呢?”记得CommunicationObject堆栈里的每个CommunicationObject对象都有一些特定的功能,并且只有栈顶的通道才可以被调用者调用。当堆栈组合正常的情况啊,GetProperty<T>方法提供了在CommunicationObject堆栈里查询特定功能的途径。例如,你也许想知道CommunicationObject堆栈是否支持特定的通道外形,MessageVersion或安全功能。下面代码演示了调用如何使用IChannel.GetProperty<T>方法:

// assume channel stack (myChannelStack) created假定通道堆栈已经创建完毕
MessageVersion messageVersion =
 myChannelStack.GetProperty<MessageVersion>();
if(MessageVersion != null){
 // do something
}
// app continues

和CommunicationObject堆栈里的其它成员一样,当一个通道不知道如何响应查询的时候,它会使用委托去调用堆栈里的下一个通道。GetProperty<T>的简单实现如下:

public override T GetProperty<T>() where T: class {
      if (typeof(T) == typeof(MessageVersion)) {
            // this type knows only how to return MessageVersion
            return (T) this.MessageVersion;
     }
      // no other capabalities are known here, so
      // delegate the query to the next node
      return this.inner.GetProperty<T>();
}

如上所示,这个GetProperty<T>方法的实现可以只返回MessageVersion,并且它的可以调用查询堆栈里的下一个通道的功能。如果查询的功能不存在,就会返回null,而不是抛出异常。因为使用了委托来嵌套查询,所以只有最底层的通道查询方法才会抛出null。

数据报通道:IInputChannel与IOutputChannel

第三张里曾经提到,数据报消息交换模式非常强大而且极具可伸缩性。在数据报消息交换模式里,发送者发送一个消息到接收者,而不期望得到回复。更简单地说,发送者输出(发送)一个消息,接收者接受一个消息作为输入。因此,WCF基础结构定义了数据报交换模式里的发送者接口名为System.ServiceModel.Channels.IOutputChannel,而接受者的接口名为System.ServiceModel.IInputChannel。

发送接口:IOutputChannel

Like its role in the Datagram MEP, the IOutputChannel interface is simple, as shown here:

和其在数据报交换模式里角色一样,IOutputChannel接口比较简单,如下所示:

public interface IOutputChannel : IChannel, ICommunicationObject {
      IAsyncResult BeginSend(Message message, AsyncCallback callback,
                             Object state);
      IAsyncResult BeginSend(Message message, TimeSpan timeout,
                             AsyncCallback callback, Object state);
      void EndSend(IAsyncResult result);
      void Send(Message message);
      void Send(Message message, TimeSpan timeout);
      EndpointAddress RemoteAddress { get; }
      Uri Via { get; }
}

首先,IOutputChannel实现了IChannel和ICommunicationObject接口。任何实现了IOutputChannel接口的类型,都必须定义公有的通道状态机成员和GetProperty<T>方法。为了支持异步编程模型(APM),接口定义了同步和异步的Send方法。

RemoteAddress属性指的是消息发送的地址。值得注意的是,这不一定是消息发送的真实地址。回忆一下第2章“面向服务”里的邮政服务的例子,这在一个消息接收者的情况下,对于标记地址十分有用。IOutputChannel上Via属性表示的另外一个地址是消息发送的目标地址。

接收接口:IInputChannel

接收数据报消息的通道实现了IInputChannel接口。对于接收者在数据报交换模式里的角色,IInputChannel只定义了接收成员而没有发送成员。IInputChannel接口的定义如下:

public interface IInputChannel : IChannel, ICommunicationObject {
 EndpointAddress LocalAddress { get; }
 // Receive Methods
 IAsyncResult BeginReceive(AsyncCallback callback, Object state);
 IAsyncResult BeginReceive(TimeSpan timeout, AsyncCallback callback,
                            Object state);
 Message EndReceive(IAsyncResult result);
 Message Receive();
 Message Receive(TimeSpan timeout);
 // TryReceive Methods
 IAsyncResult BeginTryReceive(TimeSpan timeout, AsyncCallback callback,
                               Object state);
 bool EndTryReceive(IAsyncResult result, out Message message);
 bool TryReceive(TimeSpan timeout, out Message message);
 // Waiting Methods
 IAsyncResult BeginWaitForMessage(TimeSpan timeout,
                                   AsyncCallback callback,
                                   Object state);
 bool EndWaitForMessage(IAsyncResult result);
 bool WaitForMessage(TimeSpan timeout);
}

通常,接收程序会消极地等待消息到来。为此,IInputChannel定义了三个等待消息的方法。这些方法的命名没有什么规律,但是为了简便,这里就分为Receive、TryReceive和WaitForMessage几组方法。所有的方法都包含同步和异步定义。

Receive方法等待一段时间,如果消息在这段时间内到达,该方法会返回一个Message引用。如果在规定的时间内,消息还没到达,这些方法就会抛出一个TimeoutException。TryReceive方法会等待一段时间,然后通过out参数返回一个Message引用。这些方法返回一个Boolean值表示能否在期望的时间内返回Message。Receive和TryReceive方法最大的不同就是如何显示超时结果。

与Receive和TryReceive 不同,WaitForMessage方法不会返回一个Message引用,或者一个out参数。它会返回一个表示一个消息是否到达的Boolean值。这有点像I/O基础结构里的Peek功能。把WaitForMessage与Receive或TryReceive一起使用,可以实现等待一个消息并接受一个消息。

当消息要参与到一些其他的活动中的时候,WaitForMessage方法就非常有用。比如,思考以下情况,当一个Message必须参与到一个事务里。在这个例子里,对于Receive和TryReceive方法的调用必须包装到事物里。如果Message没有到达,调用者必须终止事务。如果,调用者使用了WaitForMessage方法,这次调用就没必要发生在事务的范围内。如果WaitForMessage返回false,调用者仅仅需要再调用WaitForMessage方法。一旦Message到达,调用者能够启动一个事务,然后调用Receive或TryReceive方法执行相应的任务。

请求/应答通道:IRequestChannel和IReplyChannel

在请求/应答消息交换模式里,消息的参与者都要发送和接收消息。发送者发送消息给接收者,然后等待回复。而接收者会接收请求消息,然后发送一个回复消息。为了实现通道形状,IRequestChannel和IReplyChannel接口分别定义了符合请求/应答消息交换模式的成员.

发送接口:IRequestChannel


IRequestChannel接口定义了发送请求消息和接收应答消息的相关成员。通道层里发送和接收消息的成员都包含同步和异步的定义。如下所示:

public interface IRequestChannel : IChannel, ICommunicationObject {
 // Request Methods
 IAsyncResult BeginRequest(Message message, AsyncCallback callback,
                            Object state);
 IAsyncResult BeginRequest(Message message, TimeSpan timeout,
                            AsyncCallback callback, Object state);
 Message EndRequest(IAsyncResult result);
 Message Request(Message message);
 Message Request(Message message, TimeSpan timeout);
 EndpointAddress RemoteAddress { get; }
 Uri Via { get; }
} 

上面的代码里,Request方法接受一个Message类型的参数,然后返回一个Message类型的实例。 这些成员方法的签名保证了它们符合请求/应答消息交换模式。

接收接口:IReplyChannel

支持请求/应答消息交换模式的消息接收程序必须实现IReplyChannel接口,IReplyChannel的定义如下:

public interface IReplyChannel : IChannel, ICommunicationObject {
 RequestContext ReceiveRequest();
 RequestContext ReceiveRequest(TimeSpan timeout);
 IAsyncResult BeginReceiveRequest(AsyncCallback callback, Object state);
 IAsyncResult BeginReceiveRequest(TimeSpan timeout,
                                   AsyncCallback callback, Object state);
 RequestContext EndReceiveRequest(IAsyncResult result);
 Boolean TryReceiveRequest(TimeSpan timeout, out RequestContext context);
 IAsyncResult BeginTryReceiveRequest(TimeSpan timeout,
                                      AsyncCallback callback,
                                      Object state);
 Boolean EndTryReceiveRequest(IAsyncResult result,
                               out RequestContext context);
 Boolean WaitForRequest(TimeSpan timeout);
 IAsyncResult BeginWaitForRequest(TimeSpan timeout,
                                   AsyncCallback callback,
                                   Object state);
 bool EndWaitForRequest(IAsyncResult result);
 EndpointAddress LocalAddress { get; }
}

IReplyChannel里没有直接返回一个Message实例的成员。相反,IReplyChannel接口支持通过RequestContext类型访问接收到的Message实例。下一节会详细讨论RequestContext类型。现在,我们该知道接收到的消息对于RequestContext类型是可见的,并且可以通过RequestContext访问消息实例。

像IInputChannel一样,IReplyChannel也定义了几类接收消息的方法。ReceiveRequest方法返回一个RequestContext实例,并且超时的时候,会抛出异常。TryReceiveRequest会返回一个Boolean类型的值来表示是否在规定的时间内接收到消息。WaitForRequest方法,和IInputChannel接口上的WaitForMessage方法类似,返回的结果取决于请求消息或是否超时。

请求/应答关联:RequestContext类型

在请求/应答消息交换模式里,请求和应答是紧密关联的。从发送者的角度来看,请求通常会返回一个应答消息。从接受者的角度来看,一个接收到的消息必须产生一个应答消息。如前所述,IReplyChannel使用RequestContext作为ReceiveRequest方法的返回类型。这是请求/应答消息交换模式下,接收通道关联消息的首要方式。

更高层次上,RequestContext类型包装了请求消息,而且提供了发送应答消息给发送者的方法。在RequestContext里,可以通过RequestMessage属性查看请求消息。RequestContext的Reply方法提供了发送应答消息的途径。和其它的通道成员一样,reply方法对于同步和异步方法都是可见的。下面代码展示了RequestContext类型的成员:

public abstract class RequestContext : IDisposable {
 protected RequestContext();
 public abstract void Abort();
 public abstract void Reply(Message message);
 public abstract void Reply(Message message, TimeSpan timeout);
 public abstract IAsyncResult BeginReply(Message message,
                                          AsyncCallback callback,
                                          Object state);
 public abstract IAsyncResult BeginReply(Message message,
                                          TimeSpan timeout,
                                          AsyncCallback callback,
                                          Object state);
 public abstract void EndReply(IAsyncResult result);
 public abstract void Close();
 public abstract void Close(TimeSpan timeout);
 protected virtual void Dispose(Boolean disposing);
 void IDisposable.Dispose();
 public abstract Message RequestMessage { get; }
}

如代码所示, RequestContext实现了IDisposable接口。因为通道层里其它成员没有实现IDisposable接口,所以这里就很难看出为什么RequestContext类型会这么干。RequestContext类型实现IDisposable接口是因为RequestContext包含了一个Message实例。第4章里“WCF101”里曾经讨论过,Message实例包含一个Stream,因此必须实现IDisposable接口。因为这种关系,RequestContext类型上的Dispose方法需要调用Message实例上的Dispose方法,这样才能销毁Message 实例拥有的Stream。记住RequestContext是一个抽象类型,因此继承RequestContext的类型可以根据需要提供自己的实现。

注释:与Message类型一样,RequestContext类型明确地实现了IDisposable接口

 

 

 

双工通道:IDuplexChannel

双工通道支持双工消息交换模式(MEP)。与数据报和请求/应答消息交换模式不同的是,双工消息交换模式运行发送者和接收者自由地发送和接受消息。我们在第3章里曾经看到,双工消息交换模式里的消息通信很像电话通话。在开始通信以前,发送者和接收者必须建立通信上下文环境。在,双工消息交换模式里,发送和接受通道形状是相同的,因此,发送者和接收者实现了相同的接口(假设连个消息参与者都是WCF程序)。因为双工消息交换模式的与生俱来的自由特性,以及接收者和发送公用相同的接口,因此只能通过发起通信来区分哪个是消息的发送者(就像只能通过谁先拨号,来确定谁是打电话的人一样)。

发送和接收接口:IDuplexChannel

IDuplexChannel接口实际上是IInputChannel和IOutputChannel的结合体。如前文所述,IInputChannel是为了实现了数据报消息接收者,而IOutputChannel是为了实现数据报发送者。因为支持双工通信的通道必须能够发送和接受消息,所以逻辑上,IDuplexChannel成员是数据报交换模式里使用的所有成员合并的结果。IDuplexChannel接口的定义如下:

public interface IDuplexChannel : IInputChannel, IOutputChannel, IChannel,
 ICommunicationObject
{
}

IDefaultCommunicationTimeouts接口

因为大部分应用程序开发人员都不回直接接触通道,因此通道层必须有一种表示特定操作超时的方法。考虑到通道超时问题的时候,有4个时间敏感的操作:打开通道、发送消息、接受消息和关闭通道。和通道层的其它功能一样,WCF类型系统包含一个描述超时的接口。System.ServiceModel. IDefaultCommunicationTimeouts,定义如下:

public interface IDefaultCommunicationTimeouts {
      TimeSpan CloseTimeout { get; }
      TimeSpan OpenTimeout { get; }
      TimeSpan ReceiveTimeout { get; }
      TimeSpan SendTimeout { get; }
}

IDefaultCommunicationTimeouts接口里每个成员的作用可以从你名字里推测出来。绑定、通道工厂和通道都实现了这个接口。因为绑定、通道工厂和通道实现了相同的接口,这些类型都可以传递超时到构造链中。例如,一个用户可以在Binding里指定发送超时属性(Binding提供了setter器)。如果Binding是消息发送者的一部分,它就可以把超时属性的值通过通道工厂的构造函数传递给通道工厂。同样,通道工厂也可以把超时属性的值传递给通道的构造函数。作用上看,这一系列的传递提供给用户可以通过API指定超时属性的能力,并且这些设置可以作用于通道层上。

ChannelBase类型

所有的自定义通道必须实现公共的状态机,并且暴露GetProperty<T>查询机制,实现一个或者多个通道形状,从通道工厂里接受一个超时设置。System.ServiceModel.Channels.ChannelBase抽象类型就是这个目的,它确保了所有的通道成员的兼容性。下面代码展示了ChannelBase的类型定义:

public abstract class ChannelBase : CommunicationObject,
                                    IChannel,
                                    ICommunicationObject,
                                    IDefaultCommunicationTimeouts {
 // Constructor with channel factory parameter
 protected ChannelBase(ChannelManagerBase channelManager);
 // IChannel implementation
 public virtual T GetProperty<T>() where T: class;
 // CommunicationObject members
 protected override TimeSpan DefaultCloseTimeout { get; }
 protected override TimeSpan DefaultOpenTimeout { get; }
 protected override void OnClosed();
 protected TimeSpan DefaultReceiveTimeout { get; }
 protected TimeSpan DefaultSendTimeout { get; }
 // IDefaultCommunicationTimeouts implementation
 TimeSpan IDefaultCommunicationTimeouts.CloseTimeout { get; }
 TimeSpan IDefaultCommunicationTimeouts.OpenTimeout { get; }
 TimeSpan IDefaultCommunicationTimeouts.ReceiveTimeout { get; }
 TimeSpan IDefaultCommunicationTimeouts.SendTimeout { get; }
 // reference to channel factory
 protected ChannelManagerBase Manager { get; }
 private ChannelManagerBase channelManager;
}

ChannelManagerBase的成员表示的是工厂创建通道的方法。第7章“通道管理器”里会详细介绍这些内容。形状,假设ChannelManagerBase类型一直会从通道工厂里获取超时设置的值。注意ChannelBase里的TimeSpan类型的成员。以Default开头的成员都会从通道工厂里获取超时设置的值,而且这里明确实现了IDefaultCommunicationTimeouts的成员。如下所示:

protected override TimeSpan DefaultOpenTimeout {
 get {
   return ((IDefaultCommunicationTimeouts)this.channelManager).OpenTimeout;
 }
}
// delegate to DefaultOpenTimeout property TimeSpan
IDefaultCommunicationTimeouts.OpenTimeout {
 get {
    return this.DefaultOpenTimeout;
 }
}

上面的代码仅仅介绍了通道里open如何实现超时值的传递。close、send和receive方法实现的方式类似。

 


 

老徐的博客
【作      者】:Frank Xu Lei
【地      址】:http://www.cnblogs.com/frank_xl/
【中文论坛】:微软WCF中文技术论坛
【英文论坛】:微软WCF英文技术论坛

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值