.NET Remoting – 基础操作






4.46/5 (44投票s)
2003年10月7日
13分钟阅读

195445

9932
.NET Remoting 教程
目录
- 引言
- 目标。
- 先决条件。
- 软件包。
- 运行示例。
- 配置文件。
- 来自客户端程序集的程序集引用。
- 客户端源文件 CRemoteObjClient.cs 中的代码块
- 客户端源文件 CRemoteObjServer.cs 中的代码块
- 演练。
- 参考。
引言
.NET Remoting 是一个用于开发分布式应用程序的框架。它是 DEC/RPC/DCOM 的后继者。简单地说,Remoting 允许一个应用程序(Remoting Client)在远程服务器(Remoting server)上实例化一个类型(Remotable class)。客户端和服务器对象(Remoting Server 托管的 Remotable 类的实例)之间的通信通过“代理”(Proxy)进行——这是客户端侧的服务器对象的表示。
Remoting Server 可以是一个简单的控制台应用程序、一个 Windows 服务或托管在 IIS 中。它负责托管服务器对象并将其发布到外部世界。Remoting client 是任何使用已发布服务器对象的应用程序。
关于这个广泛的主题有很多教程。不幸的是,大多数教程在涵盖开发人员需要了解的基本操作方面,或多或少都有所欠缺。本文旨在补充 MSDN,并在一个简短的文章中提供对基本 Remoting 任务的完整介绍。
教程目标
本教程将涵盖以下主题。
- 基本 Remoting,程序化配置(通道/类型),配置文件,LeaseTime,
new
,Activator.GetObject
(服务器激活对象,或 SAO)和Activator.CreateInstance
(客户端激活对象,或 CAO) - 如何为远程类实现接口,并通过接口与远程对象交互。
- 在远程对象和客户端之间传递自定义/用户定义对象。
- 对远程对象进行异步方法调用。
- 事件和 Remoting:客户端如何订阅由远程对象生成的事件。
本教程不涵盖以下主题。
- 从 IIS 托管服务器 [参考 2, 7]
- 延迟加载通道
- 跟踪服务
ClientSponsor
和赞助机制 [参考 5]- 自定义格式化程序、接收器、通道——这些才是真正复杂的地方。[参考 1]
- SoapSuds
必备组件
- .NET 委托和事件。
- 异步编程。
IAsyncResult, BeginInvoke, EndInvoke, WaitHandle
... 等。
包
- dnetremoting.zip
整体架构
- 自述文件:dnetremoting.doc(即本文档。)
- 项目文件夹、类和整体结构
[编辑说明 - 在目录中使用连字符/空格以防止滚动]
模块 |
文件夹/目录 |
主要类 |
命名空间 |
备注 |
接口 Dll |
/CRemote-ObjInterface 源文件:IRemoteObj.cs |
|
|
用于
CProfile
演示如何将自定义对象传递给远程对象( CStatusEventSink
这是要在客户端进程中实例化的事件接收器类。它继承自 StatusEventArgs
|
远程对象 dll |
/CRemoteObj 源文件:CRemoteObj.cs |
|
|
这就是您的远程对象。此类实现 方法
属性
对于服务器激活的 对象 ID 用于通过为每个对象分配一个随机生成的 ID 来演示远程对象的生命周期。调用远程对象上的方法时,请注意服务器控制台的输出。 |
服务器 exe |
/CRemote-ObjServer 源文件:CRemote-ObjServer.cs 可执行文件 \web_vdir\bin\ CRemoteObj-Server.exe (C# 控制台应用程序) |
|
|
配置文件
选项 2 按如下方式加载配置文件
请注意,除非文件名符合以下约定,否则配置文件中的安全设置将被忽略: AssemblyName. exe.config 在这种情况下: CRemoteObjServer. exe.config
在本教程中,请将配置文件重命名为:cremoteobjserv.config 并将其放在与 exe 文件相同的文件夹(\web_vdir\bin)中。 |
客户端 exe |
/CRemote-ObjClient source CRemoteObj-Client.cs 可执行文件 \ bin\Debug \CRemoteObj-Client.exe (C# 控制台应用程序) |
|
|
注意 如果不是因为我们在 <BLOCK 2-A> 中使用了“new”关键字来实例化远程对象 那么我们可以省略对 配置文件(文件夹:/config file)
在 <BLOCK 2-A> 中,我们调用 Configure 来加载配置文件
请注意,除非文件名符合以下约定,否则配置文件中的安全设置将被忽略: AssemblyName .exe.config 在这种情况下: CRemoteObjClient .exe.config
在本教程中,请将配置文件重命名为:cremoteobjclient.config 并将其放在与 exe 文件相同的文件夹(\web_vdir\bin)中。 |
运行示例
- 运行服务器可执行文件。
- 运行客户端可执行文件。
服务器和客户端都是 C# 控制台应用程序。请注意控制台输出。
配置文件
在 Remoting 客户端和 Remoting 服务器开始通信之前,无论是在服务器端还是客户端,都必须首先正确配置两项信息:
通信通道
- 通信通道:端口号、协议、URI/URL。配置文件中的 <channel> 标签。(服务器和客户端两端)
注意:您不需要为客户端指定端口号。
要发布或使用的类型(Remotable class)
- 要发布和激活模式的资源(Remoting server 的配置文件)配置文件中的 <service> 标签(Remoting server 端)。
- 要使用的资源(Remoting client 的配置文件)配置文件中的 <client> 标签(Remoting client 端)。
两种配置选项
- 使用 XML 配置文件
RemotingConfiguration.Configure("cremoteobjserv.config");
- 以编程方式配置设置
//Configure channel: ChannelServices.RegisterChannel(httpchannel); //Register TYPE for server-activated objects (SAO): RemotingConfiguration.RegisterWellKnownServiceType( typeof(nsCRemoteObj.CRemoteObj), //Type "CRemoteObjURI", //"object URI" (NOT URL to remoting server!) WellKnownObjectMode.SingleCall //Activation mode: SingleCall or Singleton ); //Register TYPE for client-activated objects (CAO): RemotingConfiguration.RegisterActivatedType( Typeof(nsCRemoteObj.CRemoteObj) );
注意:有两种类型的服务器对象:SAO(服务器激活对象)和 CAO(客户端激活对象)。SAO 可进一步分为两种类型:singlecall
和 singleton
。以下是简要说明。
- 服务器激活对象(SAO)= 知名对象。
- 客户端激活对象(CAO)。
SAO
SingleCall
- 每次客户端通过代理调用方法时,都会创建一个新的可远程类实例。
生命周期:实例在方法调用期间保留在内存中。
这是无状态的。
Singleton
- Singleton 实例在首次方法调用时创建。
生命周期:此实例保留在内存中,直到“租约”过期。
这是有状态的。在 Remoting 服务器上只实例化一个 Singleton-SAO 实例。多个 Remoting 客户端由同一个 Singleton-SAO 提供服务。多个 Remoting 客户端可以共享一个 Singleton SAO 实例进行通信。
CAO
- 一旦 Remoting 客户端通过
Activator.CreateInstance
或new
关键字请求创建实例,它就会在远程服务器上实例化。这与 SAO(服务器对象在首次方法调用时创建)不同。 - 生命周期:与 Singleton-SAO 相同,对象保留在内存中,直到“租约”过期。
- 有状态。每个 CAO 实例仅服务于负责创建它的 Remoting 客户端。因此,如果您有多个 Remoting 客户端,每个客户端都会获得自己的 CAO 实例。
Single-call SAO 是三种选项中最具可伸缩性的,并且最适合负载均衡的环境。Singleton-SAO 和 CAO 提供了更大的灵活性,因为
- Remoting 客户端对服务器对象的生命周期拥有完全控制权。
- 服务器对象是有状态的。CAO 在多个请求/方法调用中保持状态。Singleton-SAO 在多个 Remoting 客户端之间保持状态。
示例配置文件
服务器端
服务器激活。文件名:cremoteobjserv(wellknown).config
客户端
客户端激活。文件名:cremoteobjclient(wellknown).config
除了类型和通道信息外,配置文件还可以包含对象生命周期、安全等设置。例如:Singleton SAO 和 CAO 远程对象的生命周期可以在配置文件中的 <lifetime>
标签中配置。
远程对象在其 CurrentLeaseTime
>0 的时间内有效。当对象首次实例化时,CurrentLeaseTime = leaseTime
。从这一点开始,CurrentLeaseTime
开始递减,直到下一个方法调用,或者“赞助商”延长了“租约”。在下一次方法调用时(如果租约尚未过期),CurrentLeaseTime
将重置为 renewOnCallTime
。另一方面,如果 CurrentLeaseTime
在下一次方法调用到来之前递减到零,则对象将被标记为垃圾回收。如果发生这种情况,下一次方法调用将由远程类的新实例提供服务。简而言之,“服务器激活的 Singleton”和“客户端激活”的远程对象的生命周期为 CurrentLeaseTime
>0。生命周期设置也可以通过编程方式配置。
using System.Runtime.Remoting.LifetimeServices;
LifetimeServices.LeaseTime = TimeSpan.FromMinutes(3);
LifetimeServices.RenewOnCallTime = TimeSpan.FromMinutes(3);
有关租约机制和赞助机制的更多信息,请参阅 MSDN 中的“Lifetime Leases”[参考 5] 和“Remoting Example: Lifetimes”[参考 8] 主题。
来自“客户端”程序集的程序集引用
- 引用接口程序集(即,“项目”菜单>“添加引用”>选择接口 CRemoteObjInterface.dll)
new
”关键字,我们在接口程序集(CRemoteObjInterface.dll)的引用之外,还添加了对远程对象程序集(CRemoteObj.dll)的引用。但一般来说,我们可以通过 Activator.GetObject
或 Activator.CreateInstance
检索代理,从而无需引用远程对象程序集(即,客户端仅引用接口程序集)。客户端源文件 CRemoteObjClient.cs 中的代码块
基本上,客户端源文件分为两个互斥的块——在客户端的源代码 CRemoteObjClient.cs 中通过以下标签进行标记:
<BLOCK X-Y>
... code block ...<BLOCK X-Y END>
当您想运行 BLOCK 2 中的代码时,应该注释掉 BLOCK 1,反之亦然。
- BLOCK 1:远程对象被发布为服务器激活对象。
- BLOCK 2:远程对象被发布为客户端激活对象。
因此,根据 CRemoteObjServer
中的设置,您可能希望在编译和执行之前注释掉其中一个块。此外,<BLOCK 2-A> 和 <BLOCK 2-B> 是互斥的。
- BLOCK 2-A 通过加载配置文件来配置通道/类型信息,然后使用“new”关键字实例化远程对象。
- BLOCK 2-B 通过编程方式配置通道/类型信息。通过调用
Activator.CreateInstance(..)
检索代理。
因此,再次强调,在编译和执行之前注释掉其中一个。
namespace nsCRemoteObjClient
{ //nsCRemoteObjClient
class CRemoteObjClient
{ //CRemoteObjClient
- 委托到
CRemoteObj.SetupUser()
- 签名:
public string SetupUser(string sname, string spasswd, out string sTime)
- 参考 <BLOCK 1-C>
public delegate string SetupUserDelegate(...);
[STAThread]
static void Main(string[] args)
{ //Main()
//Scenario 1: "server-activated" remote object.
<BLOCK 1>
try { //try-A<BLOCK 1-A>
检索接口/代理
- 演示如何通过
Activator.GetObject(...)
检索代理。- 将代理强制转换为
IRemoteObj
:进一步分离提供者和消费者。<BLOCK 1-A END>
<BLOCK 1-B>
- LeaseTime
- 通过接口在远程对象上调用方法。
<BLOCK 1-B END>
<BLOCK 1-C> 异步编程/Remoting
- 演示对远程对象的异步调用以及如何使用
BeginInvoke
、EndInvoke
将参数传递进出远程对象公开的方法。- 演示如何将用户定义对象传递给/从远程对象。
<BLOCK 1-C END>
} //try-A catch(Exception err) { }<BLOCK 1 END>
- 场景 2:“客户端激活”远程对象。
<BLOCK 2>
try { //try-B<BLOCK 2-A>
- 演示如何使用配置文件配置通道信息。
- 演示如何使用“
new
”关键字实例化远程对象。<BLOCK 2-A END>
<BLOCK 2-B>
演示如何通过以下方式检索到远程对象的代理:
Activator.CreateInstance(...)<BLOCK 2-B END>
<BLOCK 2-C>
- 演示如何通过代理调用方法。
- 演示如何订阅由远程对象生成的事件。
<BLOCK 2-C END>
} //try-B catch(Exception err) { }<BLOCK 2 END>
} //Main()
} //CRemoteObjClient
} //nsCRemoteObjClient
服务器源文件中的代码块
- 块 1:选项 1:通过以下方式进行通道和类型信息的编程注册:
ChannelServices.RegisterChannel
RemotingConfiguration.RegisterWellKnownServiceType
- 块 2:选项 2:通过加载 xml 配置文件进行配置
RemotingConfiguration.Configure("cremoteobjserv.config");
同样,如果您使用选项 2,请注释掉选项 1,反之亦然。
演练
- Remoting 基础,配置(通道/类型):编程方式,配置文件。LeaseTime,“
new
”,Activator.GetObject
(用于服务器激活)和Activator.CreateInstance
(用于客户端激活) - 配置文件:请参阅上一节。
LeaseTime
- 更改配置文件,并在不同的激活模式下运行应用程序:服务器激活 SingleCall,服务器激活 Singleton,客户端激活。
- 运行客户端。注释掉 <BLOCK 2> 和 <BLOCK 1-C>。请注意
obj.AuthenticateLoser
在 <BLOCK 1-B> 中被调用。 - 监控服务器控制台和
objID
。对于 Singleton,默认 LeaseTime 为 300 秒,如果方法调用之间的时间间隔大于 300 秒,当远程对象在以下模式下运行时,每次调用都会创建一个新的远程对象实例:- 服务器激活 Singleton。
- 客户端激活
租约配置可以在服务器配置文件中找到。
<lifetime leaseTime="100MS" sponsorshipTimeout="50MS"
renewOnCallTime="100MS" leaseManagerPollTime="10MS" />
在 <BLOCK 1-B> 中,连续方法调用之间的时间为 101 毫秒。略大于 100 秒。因此,每次方法调用都应由一个新实例提供服务——因此是不同的 objID
。调整租约配置并观察。对于服务器激活的 SingleCall
,每个方法调用都由一个新实例提供服务。
注意:objID
是分配给远程对象 CRemoteObj
实例的随机生成的对象 ID——请查看构造函数。
“new
”关键字:而不是使用 Activator.GetObject
或 Activator.CreateInstance
,我们可以使用“new
”来实例化远程对象。但是,如果您选择通过配置文件配置通道和类型信息,您可以使用“new”关键字来实例化远程对象。但是,“new
”意味着您将实例化 CRemoteObj
——而不是接口 IRemoteObj
。这进一步意味着您的客户端程序集必须引用 CRemoteObj
程序集。
Activator.GetObject
:请查看 CRemoteObjClient.cs <BLOCK 1-A>。
IRemoteObj obj = (IRemoteObj) Activator.GetObject(
typeof(IRemoteObj),
"https://:8085/CRemoteObjURI"
);
Activator.CreateInstance
:请查看 CRemoteObjClient.cs <BLOCK 2-B>。
//Note that it references CRemoteObjServer – not the remote object.
object[] attrs = { new UrlAttribute(
"tcp://:8086/CRemoteObjServer") };
ObjectHandle handle = (ObjectHandle) Activator.CreateInstance(
"CRemoteObj",
"nsCRemoteObj.CRemoteObj",
attrs);
CRemoteObj obj = (CRemoteObj) handle.Unwrap();
如何为远程类实现接口,并通过接口与远程对象交互。
接口:IRemoteObj
(CRemoteObjInterface.cs)
如果我们通过 Activator
的静态方法实例化远程对象,那么客户端程序集就不需要引用远程对象的程序集。这提供了远程对象和客户端之间的进一步分离。如果客户端设置是从配置文件加载的,则必须使用“new”关键字实例化远程对象。这暗示客户端必须引用远程对象的程序集。
查看源代码。注意架构和程序集引用。
在远程对象和客户端之间传递自定义/用户定义对象。
用户定义类:CProfile
(在 CRemoteObjInterface.cs 中定义)
//ATTENTION: Must be marked Serializable to pass
//user-defined object in/out of remote object.
[Serializable]
public class CProfile
{
public string fname;
public string lname;
public string user;
public string passwd;
//... other stuff...
};
方法:CRemoteObj.UpdateProfile
(在 CRemoteObj.cs 中定义)
该方法接受 CProfile
对象作为参数,并返回一个 CProfile
对象。
public CProfile UpdateProfile(CProfile p)
{
//...other stuff...
return pout;
}
该方法也由接口 IRemoteObj
公开,尽管这不是必需的。对方法的调用很简单。现在,让我们看一下客户端源文件 CRemoteObjClient.cs 中的 <BLOCK 1-C>。
CProfile p = new CProfile("John", "Kennedy", "JFK", "ace");
CProfile p2 = obj.UpdateProfile(p);
Console.WriteLine("UpdateProfile completed. return p2 passwd: {0}",
p2.passwd);
对远程对象进行异步方法调用。
查看客户端源文件 CRemoteObjClient.cs 中的 <BLOCK 1-C>。
//ATTENTION:
//Delegate for CRemoteObj.SetupUser:
public delegate string SetupUserDelegate(
string string1, string string2, out string string3);
//...other stuff...
Main(...)
{
<BLOCK 1-C>
将
SetupUser
包装在委托中,然后再调用BeginInvoke
。
SetupUserDelegate d = new SetupUserDelegate(obj.SetupUser);
异步调用
CRemoteObj.SetupUser
。string sTime; IAsyncResult ar = d.BeginInvoke( "Dexter", "AceInOpen", out sTime, null, null ); //Blocks the current thread until obj.AuthenticateLoser completes. ar.AsyncWaitHandle.WaitOne(); if(ar.IsCompleted) { //Retrieving output: //(a) "output variable" sTime //(b) "return variable" value. string ret = d.EndInvoke(out sTime, ar); Console.WriteLine("obj.SetupUser return. sTime:{0}{1}" + "return value: {2}", sTime, Environment.NewLine, ret); } //...other stuff...<BLOCK 1-C END>
//...other stuff...
}
事件和 Remoting:客户端如何订阅由远程对象生成的事件。
事件处理程序在接口模块 CRemoteObjInterface
中声明。
路由机制:当客户端调用 AuthenticateLoser
时,所发生的事件顺序如下:
就是这样。我希望本文档能全面介绍组件开发人员预期的大多数基本操作。请仔细阅读源代码并尝试不同的配置。您将能够构建分布式应用程序并充分利用 .NET Remoting 的强大功能。
参考文献
- 参考-1 .NET Remoting 定制变得容易,作者:Motti Shaked
- 参考-2 MSDN 在线:Remoting 示例:在 Internet Information Services (IIS) 中托管
- 参考-3 MSDN 在线:远程对象配置
- 参考-4 MSDN 在线:异步编程概述
- 参考-5 其他 MSDN 参考
- “异步委托编程示例”
- “异步 Remoting”
- “异步编程概述”
- “生命周期租约”
- 参考-6 SoapSuds 与 .NET Remoting 中的接口,作者:Ingo Rammer
- 参考-7 在 IIS 中托管远程对象——一个 Remoting 示例,作者:Sandeep Alur
- 参考-8 MSDN 在线:Remoting 示例:生命周期