接收器和接收器链

客户端通过向远程应用程序域发送消息来实现远程对象上的方法调用。 这是通过一组信道对象完成的。 客户端应用程序域包含一个客户端信道,而远程应用程序域则包含一个远程信道。 每个信道都由一系列在链中相互链接的信道接收器组成。 下面的插图显示了基本信道接收器链的结构。

基本信道接收器链

接收器和接收器链

信道在发送消息之前或接收消息之后沿信道接收器对象链发送每条消息。 此接收器链包含基本信道功能所需的接收器(如格式化程序、传输或堆栈生成器接收器),但您可以自定义信道接收器链,以使用消息或流执行特殊任务。 每个信道接收器要么实现 IClientChannelSink,要么实现 IServerChannelSinkfrlrfSystemRuntimeRemotingChannelsIServerChannelSinkClassTopic。 客户端上的第一个信道接收器还必须实现 IMessageSinkfrlrfSystemRuntimeRemotingMessagingIMessageSinkClassTopic。 它通常实现 IClientFormatterSink(继承自 IMessageSinkIChannelSinkBaseIClientChannelSink),被称为格式化程序接收器,因为它将传入的消息转换为流(IMessagefrlrfSystemRuntimeRemotingMessagingIMessageClassTopic 对象)。

信道接收器链会对发送到应用程序域或从应用程序域发出的任何消息进行处理。 信道接收器可以访问正在处理的消息,后续处理则使用处理后返回系统的消息。 这是一个实现记录服务或任何筛选排序的自然位置。

每个信道接收器都是首先处理流,再将流传递给下一个信道接收器,也就是说特定接收器前后的接收器都应该知道如何处理传递给它们的流。

Note注意:

消息接收器不得引发异常。 消息接收器用来控制这种状况的一种方法是将方法代码包装在 try-catch 块中。

信道接收器提供程序(实现 IClientChannelSinkProviderfrlrfSystemRuntimeRemotingChannelsIClientChannelSinkProviderClassTopicIClientFormatterSinkProviderfrlrfSystemRuntimeRemotingChannelsIClientFormatterSinkProviderClassTopicIServerChannelSinkProvider 接口的对象)负责创建处理 .NET 远程处理消息的信道接收器。 在激活远程类型时,将从信道中检索信道接收器提供程序,并在接收器提供程序上调用 CreateSink 方法,以检索链中的第一个信道接收器。

信道接收器负责在客户端和服务器之间传输消息。 它们彼此链接而形成一条链。 当在接收器提供程序上调用 CreateSink 方法时,该方法应执行下列操作:

  • 创建一个信道接收器。

  • 在链中的下一个接收器提供程序上调用 CreateSink

  • 确保下一个接收器和当前接收器链接在一起。

  • 将其接收器返回调用方。

信道接收器负责将这些接收器上执行的所有调用转发给链中的下一个接收器,同时还应当提供一种机制,用来存储对下一个接收器的引用。

对于沿接收链发送哪些内容,信道接收器有很大的灵活性。 例如,在发送实际的序列化原始消息之前协商身份验证的安全接收器可以截获完整的信道消息,用其自己的内容替换内容流,然后再沿着接收器链将内容流一直发送到远程应用程序域。 在返回过程中,安全接收器可以截获答复消息,并与远程应用程序域中对应的安全接收器进行对话。 协议一经达成,起始安全接收器便可以将原始内容流一直发送到远程应用程序域。

信道接收器链中的消息处理

一旦 .NET 远程处理系统找到可以处理消息的信道,该信道便将消息传递给格式化程序信道接收器,此过程通过调用

SyncProcessMessage(或 AsyncProcessMessage)来实现。 格式化程序接收器创建传输标头数组,并对下一个接收器调用 GetRequestStream。 此调用沿接收器链向下转发,并且任何接收器均可创建将被传递回格式化程序接收器的请求流。 如果 GetRequestStream 返回 null 引用(在 Visual Basic 为 Nothing),格式化程序接收器将创建自己的接收器以用于序列化。 此调用一经返回,即会序列化消息,并在接收器链的第一个信道接收器上调用相应的消息处理方法。

虽然接收器无法将数据写入流,但可以从流中读取数据,或沿所需位置传递新流。 接收器还可以向标头数组添加标头(如果它们以前没有在下一个接收器上调用 GetRequestStream),并在将调用转发到下一个接收器之前将它们本身添加到接收器堆栈。 (同步堆栈的作用是允许异步调用在其完成时对调用方进行回调。) 当调用到达链末端的传输接收器时,传输接收器会将标头和序列化消息通过信道发送到服务器,在该服务器上,整个过程将反向进行。 传输接收器(位于服务器上)从服务器端的流中检索标头和序列化消息,并通过接收器链转发它们,直至送达格式化程序接收器为止。 格式化程序接收器反序列化该消息并将其转发给 .NET 远程处理系统,消息在此处被恢复为方法调用,并在服务器对象上被调用。

创建信道接收器链

若要创建新的信道链,必须实现和配置 .NET 远程处理系统,以识别 IServerChannelSinkProviderIClientChannelSinkProvider 实现,您可以借助这两个实现来创建自定义的 IClientChannelSinkIServerChannelSink 实现,或检索链中的下一个接收器。 您可以使用 BaseChannelSinkWithProperties 抽象类来帮助实现自定义的信道接收器。

生成信道接收器提供程序

在构造信道时,应用程序可以将服务器或客户端信道接收器提供程序作为参数来提供。 信道接收器提供程序应当存储在一个链中,在向信道构造函数传递外部信道接收器提供程序之前,开发人员负责将所有的信道接收器提供程序链接在一起。 为此,信道接收器提供程序实现了 Next 属性。 下面的代码示例阐释如何生成客户端信道接收器提供程序。 完整的示例可以在远程处理示例:信道接收器提供程序中找到。

private Function CreateDefaultClientProviderChain() As IClientChannelSinkProvider
   Dim chain As New FirstClientFormatterSinkProvider            
   Dim sink As IClientChannelSinkProvider
   sink = chain
   sink.Next = New SecondClientFormatterSinkProvider
   sink = sink.Next
   return chain
End Function 

private IClientChannelSinkProvider CreateDefaultClientProviderChain(){
   IClientChannelSinkProvider chain = new FirstClientFormatterSinkProvider();            
   IClientChannelSinkProvider sink = chain;
   sink.Next = new SecondClientFormatterSinkProvider();
   sink = sink.Next;
   return chain;
} 
Note注意:

当在一个配置文件中提供了多个信道接收器提供程序时,.NET 远程处理系统会按照这些提供程序在配置文件中出现的顺序将它们链接在一起。 在 Configure 调用期间创建信道时,将创建信道接收器提供程序。

格式化程序接收器

格式化程序接收器会将信道消息作为实现 IMessage 的对象而序列化为消息流。 有些格式化程序接收器实现使用系统提供的格式化程序类型(BinaryFormatterfrlrfSystemRuntimeSerializationFormattersBinaryBinaryFormatterClassTopicSoapFormatterfrlrfSystemRuntimeSerializationFormattersSoapSoapFormatterClassTopic)。 其他实现可以使用自己的方法将信道消息转换为流。

格式化程序接收器的作用是生成所需的标头并将消息序列化为流。 在格式化程序接收器之后,消息将通过 SyncProcessMessageAsyncProcessMessage 调用转发给接收器链中的所有接收器。 在此阶段,消息已经序列化,不能再进行修改。

Note注意:

必须创建或修改消息的接收器本身一定要放在接收器链中的格式化程序之前。 通过实现 IClientFormatterSink 可以轻松地实现这一点,从而告诉系统它已经具有对该格式化程序接收器的引用。 然后可以将真实的格式化程序接收器放到该接收器链中后面的位置上。

在返回过程中,格式化程序接收器将消息流转换回信道消息对象(返回消息)。 客户端上的第一个接收器必须实现 IClientFormatterSink 接口。 当 CreateSink 返回信道时,返回的引用将被强制转换为 IClientFormatterSink 类型,以便可以调用 SyncProcessMessage 方法。 请记住,IClientFormatterSink 是从 IMessageSink 派生而来的。 如果强制转换失败,系统会引发异常。

自定义信道接收器

在客户端上,自定义信道接收器将被插入到格式化程序接收器与最后一个传输接收器之间的对象链中。 通过在客户端或服务器信道中插入自定义信道接收器,您可以在下面所列的时间点之一处理 IMessage

  • 在表示为消息的调用被转换为流,并通过网络发送的过程中。

  • 在流被从网络中取出,并发送至服务器或代理对象(在客户端)上远程对象前面最后一个消息接收器的过程中。

自定义接收器可以从流中读取数据或向流写入数据(具体取决于是传出调用还是传入调用),还可以根据需要将附加信息添加到标头。 在此阶段,消息已被格式化程序序列化,不能再进行修改。 当消息调用被转发到链末端的传输接收器时,传输接收器将标头写入流,并使用信道专用的传输协议将该流转发给服务器上的传输接收器。

传输接收器

传输接收器是客户端上链中的最后一个接收器,同时也是服务器上链中的第一个接收器。 除了传输序列化的消息外,传输接收器还负责将标头发送到服务器,并在服务器返回调用时检索标头和流。 这些接收器都内置于信道中,无法扩展。

替换默认的格式化程序

由于信道是抽象的网络机制,因此您可以配置 .NET 远程处理系统,将系统实现的信道与您选择的任何格式化程序结合起来使用。 为此,您可以使用带有以下参数的信道构造函数:信道属性的 IDictionary 实现、服务器上的格式化程序和客户端上的格式化程序。 您还可以在配置文件中指定格式化程序。 下面的示例指示 .NET 远程处理配置系统创建 HttpChannel,但在客户端上使用 BinaryClientFormatterSink

<configuration>
   <system.runtime.remoting>
      <application>
         <channels>
            <channel ref="http">
               <clientProviders>
                  <formatter ref="binary"/>
               </clientProviders>
         <channels>
      </application>
   </system.runtime.remoting>
</configuration> 

下面的代码以编程方式完成相同的功能,假定有一个实现 GetServerStringGetServerTime 的远程接口类型 IService

Imports System
Imports System.Collections
Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Channels
Imports System.Runtime.Remoting.Channels.Http

Public Class ClientProcess
  <MTAThread()> _
  Public Shared Sub Main()
      
    ' Note that any name/value pairs of configuration attributes can be 
    ' placed in this dictionary (the configuration system calls this same 
    ' constructor).
    Dim properties As New Hashtable()
    properties("name") = "HttpBinary"
     
    ChannelServices.RegisterChannel(New HttpChannel(properties, New BinaryClientFormatterSinkProvider(), Nothing))
    ' The last parameter above (Nothing) is the server sink provider chain 
    ' to obtain the default behavior (which includes SOAP and 
    ' binary formatters on the server side).
    Dim service As IService = CType(Activator.GetObject(GetType(IService), "http://computer:8080/SAService"), IService)
      
    Console.WriteLine("Server string is: " + service.GetServerString())
    Console.WriteLine("Server time is: " + service.GetServerTime())
  End Sub
   
End Class 

using System;
using System.Collections;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;

public class ClientProcess{

  public static void Main(string[] Args){
        
    // Note that any name/value pairs of configuration attributes can be 
    // placed in this dictionary (the configuration system calls this 
    // same HttpChannel constructor).
    IDictionary properties = new Hashtable();
    properties["name"] = "HttpBinary";

    // The last parameter below is the server sink provider chain 
    // to obtain the default behavior (which includes SOAP and binary 
    // formatters) on the server side.
    ChannelServices.RegisterChannel(new HttpChannel(properties, new BinaryClientFormatterSinkProvider(), null));

    IService service = (IService)Activator.GetObject(typeof(IService),"http://computer:8080/SAService");
        
    Console.WriteLine("Server string is: " + service.GetServerString());
    Console.WriteLine("Server time is: " + service.GetServerTime());      
  }
}

有关在 Internet 信息服务 (IIS) 中承载的这一信道和格式化程序组合的完整示例,请参见远程处理示例:在 Internet 信息服务 (IIS) 中承载

若要将此客户端更改为使用 TcpChannel 对象和 SoapClientFormatterSinkfrlrfSystemRuntimeRemotingChannelsSoapClientFormatterSinkClassTopic 对象,只需更改命名空间和 RegisterChannel 调用,如下面的代码所示:

ChannelServices.RegisterChannel(New TcpChannel(properties, New SoapClientFormatterSinkProvider(), Nothing))

ChannelServices.RegisterChannel(new TcpChannel(properties, new SoapClientFormatterSinkProvider(), null));

请参见

概念

在 Internet 信息服务 (IIS) 中承载远程对象
远程处理示例:在 Internet 信息服务 (IIS) 中承载

其他资源

高级远程处理

Footer image

版权所有 (C) 2007 Microsoft Corporation。保留所有权利。