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

.NET Remoting:使用委托处理远程事件——一个“聊天和文件发送”应用程序的真实世界示例

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.09/5 (12投票s)

2006年5月14日

CPOL

10分钟阅读

viewsIcon

144870

downloadIcon

16928

本文演示了如何使用一个实时“消息和文件传输”应用程序来处理远程对象中的事件。

引言

.NET Remoting 是一个简单的编程模型/框架,它允许来自不同机器/进程/应用程序域的对象相互通信。它提供了使用不同类型的通信协议(TCP、HTTP 等)和消息格式化程序(二进制、SOAP 等)的灵活性。不同应用程序域之间的通信由 Remoting 对象促成。Remoting 对象可以使用托管可执行文件/IIS/.NET 组件服务进行托管。运行在不同机器上的 .NET 应用程序也可以共享托管在服务器上的同一个 Remoting 对象实例。

本文演示了如何使用一个实时“消息和文件传输”应用程序来处理远程对象中的事件。

本文涵盖的技术

本文演示了以下模式和技术

  • 发布者-侦听器设计模式
  • 服务器激活的单例对象
  • 按引用编组的远程对象访问
  • 从远程对象引发事件
  • 通过 TCP 通道使用二进制格式

发布者-侦听器设计模式

设计模式是重用和共享重复问题解决方案的有效方式。“发布者-侦听器”是一种行为型设计模式。它也称为“观察者”模式。根据定义,“发布者-侦听器”模式在对象之间建立一对多依赖关系,以便当一个对象状态改变时,所有依赖者都会自动收到通知并进行更新。

Sample screenshot

如上图所示,在“发布者-侦听器”设计模式中,当发布者发布消息时,所有侦听器都会自动收到消息更新。发布者和侦听器可以是同一个对象。可以有许多发布者和侦听器。所有侦听器都必须向服务器注册,才能侦听消息。

.NET Remoting 术语

服务器激活对象

服务器激活对象是指其生命周期由服务器控制的对象。它们仅在客户端首次调用对象上的方法时,服务器根据需要创建。服务器激活对象仅支持默认构造函数。要使用带参数化构造函数的远程对象,可以使用客户端激活或动态发布(见下文)。服务器激活对象也称为周知对象类型,因为它们的 位置(URL)是预先发布和已知的。服务器激活对象有两种激活模式:单例和单次调用,这两种模式都将在下面描述。

单次调用(SingleCall)

单次调用对象只处理一个传入请求。单次调用对象适用于需要完成有限量工作的场景。单次调用对象通常不需要存储状态信息,并且它们无法在方法调用之间保持状态信息。但是,单次调用对象可以配置为负载平衡方式。

单例对象(Singleton objects)

单例对象是指服务于多个客户端并因此通过在客户端调用之间存储状态信息来共享数据的对象。它们适用于需要显式在客户端之间共享数据,以及创建和维护对象的开销很大的情况。

例如,以下代码片段描述了一个激活模式设置为 `SingleCall` 的服务器激活(周知)类型。

<service>
  <wellknown mode="SingleCall" type="Hello.HelloService, Hello" 
             objectUri="HelloService.soap" />
</service>

客户端激活对象(CAO)

客户端激活对象 (CAO) 是服务器端对象,它们在客户端请求时被激活。当客户端使用 "new" 运算符或 Activator.CreateInstance() 提交服务器对象的请求时,会向远程应用程序发送一个激活请求消息。然后服务器创建请求类的实例,并将一个 ObjRef 返回给调用它的客户端应用程序。接着在客户端使用 ObjRef 创建一个代理。客户端的方法调用将在代理上执行。客户端激活对象可以在方法调用之间为其特定客户端存储状态信息,但不能跨不同的客户端对象。每次调用 "new" 都会返回一个指向服务器类型独立实例的代理。例如,以下代码片段描述了一个客户端激活类型。请注意,我们不再需要 URL,因为对于客户端激活类型,仅类型本身就足以进行激活。此外,周知标签已被激活标签取代。

<service>
  <activated type="Hello.HelloService, Hello" objectUri="HelloService.soap" />
</service>

封送处理类型

对象仅在其创建的应用程序域中有效。除非使用以下任何一种封送处理类型,否则任何尝试将对象作为参数传递或将其作为结果返回的操作都将失败。

按值封送(MBV)

对于按值封送 (MBV) 的对象,当对象从一个应用程序传递到另一个应用程序时,会创建一个完整的对象副本。如果对象被标记为 Serializable,对象将自动序列化,从一个应用程序域传输到另一个应用程序域,然后反序列化以在第二个应用程序域中生成一个精确的对象副本。

按引用封送(MBR)

对于按引用封送 (MBR) 的对象,当从一个应用程序传递到另一个应用程序时,会创建对象的引用。当对象引用 (ObjRef) 到达远程应用程序时,它会变成原始对象的“代理”。为了按引用封送 Remoting 对象,请从 MarshalByRefObject 类继承它。

代理对象

当客户端创建远程对象实例时,它会收到服务器上类实例的代理。所有在代理上调用的方法都将自动转发到远程类,并且任何结果都将返回给客户端。从客户端的角度来看,这个过程与进行本地调用没有什么不同。

无状态和有状态对象

.NET Framework 提供了将远程对象创建为无状态的机制。当对象配置为 SingleCall 时,它会在调用该对象上的方法时创建。该对象处理调用,返回一个可选结果,然后由垃圾回收器回收。这样,客户端在每次调用时都连接到全新的对象。

将对象配置为单例可确保在每次调用该对象时,所有客户端都将连接到同一个对象。

基于租约的生命周期

远程对象的生命周期由租赁机制控制。当对象首次创建时,它会获得一个租用时间。当对象的租用时间归零时,对象将从 Remoting 基础设施中断开连接,当 AppDomain 中所有对该对象的引用都被释放后,它将被垃圾回收器回收。提供了许多机制允许客户端延长对象的租用时间,从而维持其生命周期。

从远程对象引发事件

与任何普通的 .NET 类一样,事件处理程序可以附加到 Remoting 对象,以便客户端可以挂接方法来处理声明的事件。每当远程对象中触发事件时,事件处理程序方法就会在客户端中执行。然而,委托要求接收对象能够获取其函数被委托包装的类的类型信息。在 Remoting 的情况下,这意味着客户端程序集必须对服务器可用。如果客户端程序集对服务器不可用,则无法加载该类型信息。

为了使远程委托正常工作,必须在客户端和服务器都可访问的公共程序集中定义一个包含回调函数的抽象类(Visual Basic .NET 中的 MustInherit,C# 中的 abstract)。然后,客户端可以从这个抽象类派生一个自定义类来实现回调中的逻辑。抽象类需要具有特定的结构。用于回调的函数必须是公共函数,且不能被重写。此函数必须将所有调用转发到在派生客户端类中被重写的受保护抽象函数。这种架构的原因是委托需要能够绑定到回调函数的具体实现,并且该实现不能被重写。

远程委托在“消息和文件传输应用程序”(在第6节中解释)中实现。您可以参考随附的源代码。

消息和文件传输应用程序

问题定义

基本上,它是一个为 Windows 网络内部网用户设计的聊天应用程序。以下截图解释了该工具所需的功能。当用户执行客户端应用程序时,用户会自动登录到应用程序。然后加载一个 WinForm,其中包含所有已登录用户的列表。此列表会频繁自动更新。要与用户聊天,您只需选择用户名并在可编辑文本框中输入消息。输入消息后,它会与“发件人”和“收件人”地址一起发布到远程对象,所有侦听客户端都会收到。收到消息后,每个客户端都会检查“收件人”地址并决定是否显示该消息。

步骤 1

用户“Joser”登录

Sample screenshot

第二步

用户“Vijiths”登录并向用户“Joser”发送了一些消息和文件

Sample screenshot

Sample screenshot

步骤 3

用户“Joser”收到下载确认消息

Sample screenshot

Sample screenshot

Sample screenshot

解决方案架构

解决方案架构如下图所示。有一个类的对象(远程对象)通过控制台应用程序托管在远程服务器上。此应用程序的加载可以通过 Windows 服务进行配置。与任何客户端-服务器应用程序一样,客户端在访问服务器应用程序之前,服务器应用程序必须启动并运行。此对象是一个单例对象。客户端应用程序是一个 WinForm,它引用了远程组件的本地副本。使用本地引用,客户端应用程序创建一个代理对象。然后,此代理对象获取远程对象的引用,以便当客户端访问代理对象时,它会反过来访问远程对象。由于远程对象是单例对象,因此连接到单个服务器的所有客户端都访问远程类的相同实例。这促进了不同客户端之间的通信。

远程对象公开了某些事件处理程序,例如 OnLoginOnLogoutOnMessagePublish 等。客户端应用程序可以将方法挂接到这些事件处理程序。因此,每当这些事件发生时,所有客户端都会收到通知。

每当用户向另一个用户发送消息时,都会触发 OnMessagePublish 事件。发送者充当发布者。所有已向 OnMessagePublish 事件处理程序注册(添加)方法的客户端都充当侦听器。这就是此设计中“发布者-侦听器”设计模式的实现方式。

Sample screenshot

配置文件

服务器配置文件

<configuration>
  <system.runtime.remoting>
      <application>
       <lifetime leaseTime="20D" sponsorshipTimeout="1H" 
                 renewOnCallTime="1D" leaseManagerPollTime="1H" />
         <service>
            <wellknown
               mode="Singleton"
               type="MessageShare.SharedMessage,MessageShare"
               objectUri="SharedMessage"/>
         </service>
         <channels>
           <channel ref = "tcp" port = "8080"> 
             <serverProviders>
               <formatter ref="binary" typeFilterLevel="Full" />
             </serverProviders>  
           </channel>
         </channels>
      </application>
  </system.runtime.remoting>
</configuration>

客户端配置文件

<configuration>
   <system.runtime.remoting>
      <application>
      <lifetime leaseTime="20D" sponsorshipTimeout="1H" 
               renewOnCallTime="1D" leaseManagerPollTime="1H" />
        <client>
        <wellknown type="MessageShare.SharedMessage,MessageShare" 
              url="tcp://10.201.33.229:8080/SharedMessage" />                                        
        </client>
         <channels>
            <channel ref="tcp" port="0" clientConnectionLimit="20" >
           </channel>
         </channels>        
       </application>
   </system.runtime.remoting>
  <appSettings>
    <add key="RemotingUrl" value="tcp://10.201.33.229:8080/SharedMessage"></add>
  </appSettings>
</configuration>

请记住将 IP 地址和端口号 - tcp://10.201.33.229:8080 更改为运行服务器应用程序的服务器机器的 IP 地址和端口号。端口号应与服务器配置文件中配置的端口号相同。

代码片段

Remoting 类

服务器应用程序

客户端应用程序 (WinForm)

安装文件

安装说明

  • 请记住将客户端配置文件中的 IP 地址和端口号 - tcp://10.201.33.229:8080 更改为服务器应用程序运行的服务器机器的 IP 地址和端口号。
  • 客户端配置文件中的端口号应与服务器配置文件中配置的端口号相同。
  • 手动启动服务器应用程序(可执行控制台应用程序)。
  • 仅在确保以下事项后才使用客户端应用程序:
    • 配置文件已正确更新。
    • 服务器应用程序已启动。

参考

缩写

缩写 展开
MBV 按值封送
MBR 按引用封送
CAO 客户端激活对象
SOAP Simple Object Access Protocol
IIS Internet 信息服务
© . All rights reserved.