A Pinch Over .NET Remoting






3.29/5 (13投票s)
2004年9月1日
9分钟阅读

82873

700
本文简要介绍 .NET Remoting。
引言
随着时间的推移,构建应用程序的观念正在迅速改变,无论是桌面应用程序、Web 应用程序还是分布式应用程序。如今,将应用程序构建为一组分布在计算机网络上的组件,这些组件协同工作,就好像所有组件都来自同一台计算机一样,已成为一种惯例。传统上,以 DCOM、CORBA 或 RMI 而闻名的分布式应用程序逻辑为满足不断增长的应用程序需求奠定了可靠且可扩展的平台。
这些基于组件的技术在内网环境中效果非常好。唯一的问题是,我们无法在 Internet 上使用这些技术,因为这些技术不兼容。
Web 服务与 Remoting 比较
相比之下,基于浏览器的 Web 应用程序是松散耦合的,并且具有惊人的互操作性。它们使用 HTTP 进行通信,以多种格式交换 MIME 类型的数据。Web 服务改编了传统的 Web 编程模型,可供各种应用程序使用,而不仅仅是基于浏览器的应用程序。它们使用 HTTP 和其他 Internet 协议交换 SOAP 消息。由于 Web 服务依赖于行业标准,包括 HTTP、XML、SOAP 和 WSD,以在 Internet 上公开应用程序的功能,因此它们独立于编程语言、平台和设备。
这要归功于 Microsoft 发明了 ASP.NET Web 服务和 .NET Remoting。Web 服务基础结构提供了一个简单的 API,用于基于将 SOAP 消息映射到方法调用来构建 Web 服务。这可以通过提供一个非常简单的编程模型来实现,该模型基于将 SOAP 消息交换映射到单个方法调用。Web 服务的客户端不必了解用于构建它们的平台、对象模型或编程语言(反之亦然,即服务也不知道发送消息的客户端)。唯一的要求是双方都应遵循关于所生成和使用的 SOAP 消息格式的协议。
Remoting 架构
.NET Remoting 提供了一个分布式对象的基础结构。它使用灵活且可扩展的底层机制,将 .NET 的完整对象语义暴露给远程进程。.NET Remoting 提供了更复杂的功能,包括按值或按引用传递对象、回调以及多对象激活和生命周期管理策略的支持。为了使用 .NET Remoting,客户端需要使用 .NET 构建。
简单来说,使用对象引用在服务器对象和客户端之间进行通信是 Remoting 的核心。然而,Remoting 架构为程序员提供了一个更简单的过程。如果任何人正确配置了客户端,我们只需要使用 `new` 关键字创建远程对象的新实例,然后客户端就会收到服务器对象的引用,其余的操作与对象在您的进程中一样(例如调用方法),即使它正在独立的计算机上运行。
假设我们有一个运行在一台计算机上的应用程序,并且我们想使用另一台计算机上存储的类型的公开功能;下面是一个典型的 Remoting 场景
数据封送
数据是如何被封送的。我们将在下面讨论同样的问题
序列化和元数据
所有分布式通信的底层机制最终都会做两件事:
- 将程序化数据类型的实例封送为可以跨网络发送的消息,这是通过某种形式的序列化引擎或封送器来实现的。
- 提供这些消息外观的描述,这是通过某种形式的元数据实现的。
例如,对于大多数 DCOM 接口,序列化引擎是类型库封送器,类型库提供元数据。
ASP.NET Web 服务和 .NET Remoting 之间的关键区别在于它们如何将数据序列化为消息以及它们为元数据选择的格式。
NET Remoting 如何封送数据
.NET Remoting 依赖于 `System.Runtime.Serialization` 引擎使用的 `IFormat` 接口的可插入实现,用于将数据封送进出消息。.NET Framework 提供了两个标准的封送器:
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
System.Runtime.Serialization.Formatters.Soap.SoapFormatter
顾名思义,`BinaryFormatter` 和 `SoapFormatter` 分别以二进制和 SOAP 格式封送类型。对于元数据,.NET Remoting 依赖于 CLR 程序集,这些程序集包含关于它们实现的各种数据类型的所有相关信息,并通过反射公开。对程序集进行元数据依赖,可以轻松地保留完整的运行时类型系统可靠性。因此,当 .NET Remoting 封送数据时,它会包含类的所有公共和私有成员。
然而,如上所述,依赖运行时元数据进行封送也限制了 .NET Remoting 系统的覆盖范围,因为客户端必须理解 .NET 构造才能与 .NET Remoting 端点通信。
通道
作为一种附加功能,.NET Remoting 层支持可插入的通道来发送消息。有两种标准的通道用于消息传输,独立于格式(即二进制格式或 SOAP 格式)。TCP 通道和 HTTP 通道都提供了发送方-接收方通道的实现,该通道使用 HTTP 协议传输消息。
.NET Remoting 对象
如上所述的状态管理选项,有三种类型的对象可以配置为充当 .NET 远程对象。根据应用程序的要求选择对象类型。
- 单次调用 (Single Call):单次调用对象仅服务一个传入的请求。当对象需要执行有限的工作时,单次调用对象很有用。单次调用对象不需要存储状态信息;事实上,它们在方法调用之间无法保留状态信息。
- 单例对象 (Singleton Objects):这些对象服务多个客户端,因此通过在客户端调用之间存储状态信息来共享数据。当需要显式地在客户端之间共享数据时,它们很有用。
- 客户端激活对象 (Client-Activated Objects):这些是服务器端对象,它们在客户端请求时被激活。当客户端使用“new”运算符提交服务器对象请求时,一个激活请求消息会被发送到远程应用程序。然后服务器创建所请求类的实例,并将一个 `ObjRef` 返回给客户端,然后使用该 `ObjRef` 创建代理。这些对象可以在其特定客户端的方法调用之间存储状态信息。每次调用“new”都会返回一个指向服务器类型独立实例的代理。
远程对象的生命周期
通常,对于具有传输到应用程序外部的对象引用的对象,会创建一个租约。租约有租约时间;当租约达到零时,它就会过期,对象将与 .NET Remoting Framework 断开连接。一旦 AppDomain 中所有对该对象的引用都已释放,该对象将在下一次垃圾回收发生时被收集。租约控制对象的生命周期。
总结要点
- .NET Remoting 支持运行时类型系统,并提供更复杂的编程模型,但覆盖范围更有限。
- .NET Remoting 允许将远程对象托管在任何类型的应用程序中,包括 Windows 窗体、托管 Windows 服务、控制台应用程序或 ASP.NET 工作进程。通道(TCP 和 HTTP)都通过套接字提供发送和接收进程之间的通信。
- .NET Remoting 基础结构是可扩展的。可以过滤入站和出站消息,控制类型封送和元数据生成的各个方面。可以使用 .NET Remoting 实现自定义封送器和通道。
- 托管在 IIS 中的 .NET Remoting(与 ASP.NET 一起)可以利用 ASP.NET Web 服务的所有安全功能。如果我们使用托管在 `aspnet_wp.exe` 以外进程中的 TCP 或 HTTP 通道,则我们必须自己实现身份验证、授权和隐私机制。
- .NET Remoting 支持一系列状态管理选项(取决于对象生命周期方案:单次调用或单例对象)。
- 在性能方面,当我们使用 TCP 通道和二进制封送器时,.NET Remoting 提供最快的通信。
构建示例应用程序
在下面的示例中,远程对象公开了两个方法,用于对给定两个数字进行加法和减法。
构建一个使用 .NET Remoting 在应用程序域之间进行通信的应用程序非常直接。
- 您必须有一个可远程类型的实现。
- 一个监听或宿主应用程序域。
- 一个客户端或调用应用程序域。
- 并且您必须在每个应用程序域中配置 remoting 系统,以便为可远程类型使用远程激活。
无论 remoting 场景有多么复杂或简单,上述过程都适用。
我们在此讨论以上每个要点。
构建可远程类型
我们将简要讨论如何构建可远程类型。为了让其他应用程序域中的对象使用类的实例,该类必须继承自 `MarshalByRefObject`。以下代码示例显示了一个可以在另一个应用程序域中执行的对象调用的简单对象。
MathLibrary.cs
using System;
public class MathLibrary : MarshalByRefObject
{
private int result;
public int AddTwoNumber(int num1, int num2)
{
result = num1 + num2;
return result;
}
public int SubtractTwoNumber(int num1, int num2)
{
result = num1 - num2;
return result;
}
}
将上述文件保存为 `MathLibrary.cs` 并放在您自己的目录中。从命令提示符像下面这样将此文件编译为 DLL:
csc /noconfig /t:library MathLibrary.cs.
构建宿主应用程序
仅仅创建 `MathLibrary` 并不意味着我们的工作就完成了。要远程创建此对象的实例,您必须构建一个宿主或监听应用程序,该应用程序执行两项操作:
- 选择并注册一个通道,这是一个处理网络协议和序列化格式的对象。
- 将您的类型注册到 .NET Remoting 系统,以便它可以使用您的通道监听您的类型的请求。
由于远程配置是基于每个应用程序域进行的,因此必须运行应用程序域才能监听请求。
有一点很重要,与 COM 不同,Remoting 不会自行启动宿主或服务器应用程序。
以下代码实现了一个简单的 `MathLibrary` 宿主应用程序域,它使用配置文件。
Listener.cs
using System;
using System.Runtime.Remoting;
public class Listener
{
public static void Main()
{
RemotingConfiguration.Configure("Listener.exe.config");
Console.WriteLine ("Listening for requests. Press Enter to exit...");
Console.ReadLine();
}
}
将上述代码保存为 `Listener.cs` 并放在创建 `MathLibrary.dll` 的同一目录中。像下面这样编译 `Listener.cs` 并引用 `MathLibrary.dll`:
csc /noconfig /r:MathLibrary.dll Listener.cs.
由于上面的代码片段使用 `Listener.exe.config` 文件来监听其可远程类型,因此我们需要在创建 `Listener.exe` 的同一目录中创建 `Listener.exe.config` 文件。
Listener.exe.config
<configuration>
<system.runtime.remoting>
<application>
<service>
<wellknown
mode="Singleton"
type="MathLibrary, MathLibrary"
objectUri="MathLibrary.rem"
/>
</service>
<channels>
<channel ref="http" port="8989"/>
</channels>
</application>
</system.runtime.remoting>
</configuration>
构建客户端应用程序
到目前为止,我们已经创建了 `MathLibrary` 和 Remoting 的宿主应用程序。我们的应用程序必须将自己注册为远程对象的客户端,然后像它驻留在客户端应用程序域中一样调用它。.NET Remoting 系统会拦截客户端调用,将其转发到远程对象,并将结果返回给您的客户端。
Client.cs
using System;
using System.Runtime.Remoting;
public class Client
{
public static void Main()
{
RemotingConfiguration.Configure("Client.exe.config");
MathLibrary lib = new MathLibrary();
Console.WriteLine("Enter Number1:");
string num1 = Console.ReadLine();
Console.WriteLine("Enter Number2:");
string num2 = Console.ReadLine();
Console.WriteLine(lib.AddTwoNumber(Convert.ToInt16(num1),
Convert.ToInt16(num2)).ToString());
}
}
将上述代码保存为 `Client.cs` 并放在创建 `Client.exe` 的同一目录中。像下面这样编译 `Client.cs` 并引用 `MathLibrary.dll`:
csc /noconfig /r:MathLibrary.dll Listener.cs
由于上面的代码片段使用 `Client.exe.config` 文件来监听其可远程类型,因此我们需要在创建 `Client.exe` 的同一目录中创建 `Client.exe.config` 文件。
Client.exe.config
<configuration>
<system.runtime.remoting>
<application>
<client>
<wellknown
type="MathLibrary, MathLibrary"
url="https://:8989/MathLibrary.rem"
/>
</client>
</application>
</system.runtime.remoting>
</configuration>
现在,一切都已准备好运行您的应用程序。
从命令提示符(从创建 `Client.exe` 和 `Listener.exe` 的目录),输入 `Listener.exe`。