65.9K
CodeProject 正在变化。 阅读更多。
Home

.NET Remoting 窥探

starIconstarIconstarIconemptyStarIconemptyStarIcon

3.00/5 (4投票s)

2001 年 1 月 11 日

viewsIcon

124310

downloadIcon

1397

解释了 Microsoft 的 RemSpy 示例的实现,以及更多关于 Remoting 的解释。

  • 下载演示项目 - 32 Kb
  • 本文旨在分享我在阅读 Microsoft 关于 Remoting 的文章以及随附的 .NET SDK Beta 1 文档时获得的经验。在探索的过程中,我发现了一些关于 Remoting 工作原理的有趣之处。可以说,事情现在开始变得有意义了。然后,我查看了 SDK 提供的一些示例。这些示例有助于实现一些基本功能,例如创建远程服务器对象并让客户端访问它们。有趣的是,有一个名为 RemSpy 的示例,据说可以监视对远程对象的调用。查看 RemSpy 的代码并没有帮助我理解这些内容。我留下了一大堆问题。这是什么对象?为什么它继承自那个接口?为什么我必须将某些对象注册到 Remoting 服务?因此,我必须深入了解 Remoting 架构。这是一次有趣且有价值的经历。现在我对调用如何从客户端移动到服务器再返回有了一些了解。在本文中,我不会详细介绍基础知识。 Microsoft 有一些很棒的文章供您参考。

    基础知识在哪里可以找到

    • Microsoft .NET Remoting:技术概述
    • .NET SDK 文档
    • Remoting 示例

    基础

    在我解释这个 spy 示例的工作原理之前,我会简要介绍一下调用的 Remoting 是如何工作的。两个应用程序之间通信的基本构建块是由 IMessage 接口定义的 message 对象。简单来说,这种通信可以描述如下:

    1. 应用程序 A 创建一个消息信封,并将所有信息放入该消息中。
    2. 然后应用程序 A 将消息信封交给一个载体。
    3. 载体将消息信封带到应用程序 B。
    4. 应用程序 B 打开信封,读取消息,并根据消息的描述采取必要的行动。
    5. 如果应用程序 A 正在等待回复,那么应用程序 B 将该信息或回复放入消息信封中。
    6. 应用程序 B 将信封交给载体。
    7. 最后,载体将消息送回应用程序 A。

    从上面的流程来看,如果我们能够截获各种调用,我们就可以查看消息信封内部以及执行其他相关任务。

    如何截获调用

    当客户端创建远程对象时,它会获得一个实际实例或一个代理,具体取决于客户端运行的 AppDomain。如果客户端运行的 AppDomain 与服务器不同,它将获得一个代理。现在,当客户端在代理上调用方法时,所有信息都会被打包到一个 message 对象 IMessage 中。然后将其传递给消息接收器 (message sink)。消息接收器使用通道将消息从源传输到目的地。当服务器需要返回结果时,它会遵循相同的过程。输出参数或结果被放入消息中并发送到消息接收器。然后接收器使用通道将结果传播回源,即客户端。整个机制基于将消息从一个消息接收器发送到另一个消息接收器。每个消息接收器完成其工作并将消息发送到下一个接收器。如果我们可以在这个接收器链中插入一个接收器,我们就可以截获调用并查看在操作的各个阶段正在传递什么。

    .NET Remoting 提供了一些可以用于监视对象进出调用的接口。动态属性 (Dynamic Properties) 和动态接收器 (Dynamic Sinks) 是帮助截获调用的两个主要接口。服务器需要做的是注册一个动态属性。动态属性将实现 IDynamicPropertyIContributeDynamicSink 接口。

    public class NKRemoteServerDynamicProperty : IDynamicProperty, IContributeDynamicSink
    .
    .
    dynamicProp = new NKRemoteServerDynamicProperty ();
    Context.RegisterDynamicProperty (dynamicProp, null, null);

    RegisterDynamicProperty 调用的第一个参数是将被注册到框架的动态属性。第二个参数是注册属性的对象。最后一个参数是属性注册的上下文。有关此调用的更多详细信息,请参阅文档。

    IContributeDynamicSink 接口只有一个方法,GetDynamicSink。此方法应返回在对象上进行的 Remoting 调用将被通知的接收器。因此,在此方法中,我们将创建一个接收器并将其返回给框架。

    public IDynamicMessageSink GetDynamicSink ()
    {
       return new NKRemoteServerDynamicMessageSink ();
    }

    要提供我们自己的接收器,我们将必须创建一个派生自 IDynamicMessageSink 的自定义接收器对象。

    public class NKRemoteServerDynamicMessageSink : IDynamicMessageSink

    正是这个消息接收器对象帮助截获对对象的所有调用,以及从对象发出的所有调用。此接口只有两个方法,ProcessMessageStartProcessMessageFinish。顾名思义,当消息开始和结束时,接收器会收到通知。这些方法的签名非常简单,它们只有三个参数。

    public void ProcessMessageStart (IMessage reqMsg, bool bClientSide, bool bAsync)
    public void ProcessMessageFinish (IMessage replyMsg, bool bClientSide, bool bAsync)

    第一个参数是 IMessage 对象。如果这是一个 MessageStart 调用,那么此对象包含从客户端发送的信息。您可以查看 IMessage 对象的参数和属性,以了解客户端如何将信息放入消息中,以便在服务器端重建堆栈。IMessage 对象包含 InArgsOutArgsMethodNameMethodSignatureContext 等属性。从这些属性中,我们可以获得方法调用的所有详细信息。如果调用是 MessageFinish,则 IMessage 对象包含来自服务器端的响应。同样,我们可以查看消息对象以了解服务器如何将堆栈返回给客户端。第二个参数指示调用是在服务器端还是客户端。最后一个参数指示调用是否为异步模式。所有这些调用的实现都已在源文件 NKRemoteServer.cs 中提供。

    如何找出对象何时被封送和解封

    我们可以找出对象何时被封送、解封和断开连接。此信息由 TrackingServices 提供。我们可以向 TrackingServices 注册一个跟踪处理程序来截获这些调用。我们可以提供自己的对象来处理跟踪。这是通过创建一个派生自 ITrackingHandler 接口的类来实现的。

    public class NKRemoteServerTrackingHandler : ITrackingHandler

    此类有三个必须实现的方法:MarshaledObjectUnmarshaledObjectDisconnectedObject。当对象被封送、解封和断开连接时,会分别调用这些方法。我们可以简单地转储传递给这些方法的 ObjectObjRef 对象的信息,以查看每个对象包含哪些信息。

    要注册跟踪处理程序,请在 TrackingServices 上调用 RegisterTrackingHandler 函数。

    trkHndlr = new NKRemoteServerTrackingHandler ();
    TrackingServices.RegisterTrackingHandler (trkHndlr);

    当我们完成跟踪处理程序的使用后,通过调用 UnregisterTrackingHandlerTrackingServices 中注销它。

    TrackingServices.UnregisterTrackingHandler (trkHndlr);

    尽管 SDK 文档没有提及,但 UnregisterTrackingHandler API 尚未实现。当你调用这个函数时,会抛出一个异常,说这个方法还没有实现。

    实用工具库

    在查看各种 Microsoft Remoting 示例时,我遇到了一些非常有助于转储 Remoting 对象信息的实用函数。我修改了这些函数,并将更多函数添加到一个实用工具类中。这个类库项目 RemoteUtils 包含在本文的源代码中。您需要此库来编译本文中包含的服务器和客户端代码。此实用工具库实现的方法如下。

    DumpMessageObj    - 转储 IMessage 对象信息
    DumpObjectRef     - 转储 ObjRef 信息
    DumpChannelObj    - 转储 IChannel 对象信息
    DumpLeaseObj      - 转储 ILease 对象信息
    DumpChannelSinkProperies

    随着时间的推移,我将向这个实用工具类添加更多函数。

    源代码和编译

    服务器对象的源代码在 NKRemoteServer.cs 中。客户端应用程序代码在 RemoteClientApp.cs 中。实用工具库代码在 RemoteUtils.cs 中。客户端配置文件是 RemoteClient.cfg。此文件需要复制到您运行客户端的任何位置。该项目是使用 VS.Net Beta 1 创建的。主项目文件 UtilServer.sln 位于 UtilServer 文件夹中。确保在运行客户端之前启动服务器。否则,客户端应用程序将抛出异常。要测试示例,请打开两个 DOS 窗口并启动服务器和客户端应用程序。您将看到调用是如何被截获的,以及在客户端和服务器端的各个阶段发出的所有信息。这是一个非常有趣的信息内容。大部分实现都受到了 SDK 中包含的 RemSpy 示例的启发。我还扩展了这个示例来监视 Proxy 对象。我将在另一篇文章中介绍这一点。如果您想在另一台机器上运行服务器,请将 RemoteClient.cfg 文件中 RemoteApplication 部分的条目更改为如下所示:

    RemoteApplication#NKRemoteServer#tcp://:8085/NKRemoteServer
    
    To
    
    RemoteApplication#NKRemoteServer#tcp://127.0.0.1:8085/NKRemoteServer

    相信我,它确实有效。无需多言,您需要安装 .NET Framework 才能编译和运行这些示例。实现是用 C# 完成的。

    © . All rights reserved.