.NET Remoting
.NET Remoting是微软随.NET推出的一种分布式应用解决方案,被誉为管理应用程序域之间的 RPC 的首选技,它允许不同应用程序域之间进行通信(这里的通信可以是在同一个进程中进行、一个系统的不同进程间进行、不同系统的进程间进行)。更具体的说,Microsoft .NET Remoting 提供了一种允许对象通过应用程序域与另一对象进行交互的框架。也就是说,使用.NET Remoting,一个程序域可以访问另外一个程序域中的对象,就好像这个对象位于自身内部,只不过,对这个远程对象的调用,其代码是在远程应用程序域中进行的,例如在本地应用程序域中调用远程对象上一个会弹出对话框的方法,那么,这个对话框,则会在远程应用程序域中弹出。
.NET Remoting框架提供了多种服务,包括激活和生存期支持,以及负责与远程应用程序进行消息传输的通讯通道。格式化程序用于在消息通过通道传输之前,对其进行编码和解码。应用程序可以在注重性能的场合使用二进制编码,在需要与其他远程处理框架进行交互的场合使用 XML 编码。在从一个应用程序域向另一个应用程序域传输消息时,所有的 XML 编码都使用 SOAP 协议。出于安全性方面的考虑,远程处理提供了大量挂钩,使得在消息流通过通道进行传输之前,安全接收器能够访问消息和序列化流。
.NET Remoting协同工作能力
下图是.NET Remoting的体系结构图
.NET Remoting通信体系结构
一般来说,.NET Remoting包括如下几点主要元素:
Ø 远程对象:运行在Remoting服务器上的对象。客户端通过代理对象来间接调用该对象的服务,如上图的“通信体系结构”所示。在.NET Remoting体系中,要想成为远程对象提供服务,该对象的类必须是MarshByRefObject的派生对象。另外,要说明的是,需要在网络上传递的对象,例如“参数”,则必须是可序列化的。
Ø 信道:信道是服务器和客户机进行通信用的(这里的服务器和客户机并不一定都是计算机,也可能是进程)。在.NET Remoting中,提供了三种信道类型:TCP、HTTP、IPC,另外,也可以定制不同的信道以适应不同的通信协议。
Ø 消息:客户机和服务器通过消息进行信息交换,消息在信道中传递。这里的消息包括,远程对象的信息,调用方法名称,参数,返回值等。
Ø 格式标识符:该标识符标明了消息是按照什么样的格式被发送到信道上的,目前.NET 2.0提供了两种格式标识符:SOAP格式和二进制格式。SOAP格式标识符符合SOAP标准,比较通用,可以和非.NET 框架的Web服务通信。二进制格式标识符,则在速度、效率上面更生一筹,但通用性较SOAP差。另外,Remoting还支持自定义的格式标识符。(顺便说一下:TCP信道,默认使用二进制格式传输,因为这个效率更高;Http信道则默认使用SOAP格式;不过在系统中,哪种信道具体使用哪种格式,则是可以根据需要设置的。)。
Ø 格式标识符提供程序:它用于把格式标识符和信道联系起来。在创建信道时,可以指定所要使用的标识符提供程序,一旦指定了提供程序,那么消息被发送到信道上的格式也就确定了下来。
为序列化消息, .NET Remoting 提供了两类格式程序接收器: BinaryFormatter和SoapFormatter。选择的类型很大程度上取决于连接分布式对象的网络环境的类型。由于. NET Remoting体系结构的可插入特性,可以创建自己的格式程序接收器,并插入到.NET Remoting基础设施中。这种灵活性使基础设施能够支持可能的各种线路格式。
对于可以发送并接收二进制数据(例如TCP/IP)的网络传输协议,可以使用System.Runtime.Serialization.Formatters.Binary名字空间中定义的BinaryFormatter类型。顾名思义,BinaryFormatter将消息对象序列化为一个二进制格式的流。这是消息对象在线缆间进行传输的最有效而简洁的表示方式。一些网络传输系统不允许发送和接收二进制数据。这类传输迫使应用程序在发送之前将所有的二进制数据转换成ASCII文本表示形式。在这种情况下(或者要得到最佳协作能力的时候),.NET Remoting在System.Runtime.Serialization.Formatters.Soap名字空间中提供SoapFormatter类型。SoapFormatter使用消息的SOAP表示形式将消息序列化为流。
Ø 代理对象:前面也说过,客户端不能直接调用远程对象,客户机只能通过代理对象来操作远程对象。代理对象,又分为透明代理和真实代理。在客户机看来,代理对象和远程对象是一样的。客户机调用透明代理对象上的方法,透明代理再调用真实代理上的Invoke方法,Invoke方法再使用消息接受器把消息传递到信道上。
下图是客户机的方法调用导致消息在信道间传递的一个体系结构图:
消息在信道上的传递过程
Ø 消息接受器:如上图所示,消息接受器在服务器端和客户端都有,接受真实代理的调用,把序列化的消息发布到信道上。
Ø 激活器:这涉及到对象生命期管理,客户机使用激活器在服务器上创建远程对象,或者说是申请一个远程对象的引用。
Ø RemotingConfiguration类:该类用于配置远程服务器和客户机的一个实用类,它可以用于读取配置文件或者动态地配置远程对象。说明一点的是:RemotingConfiguration类中的大部分属性、方法都是静态的,这就意味着很多属性,如应用程序名称,只能通过当前属性或配置文件设置一次。如果应用程序运行在宿主环境中,例如 Internet 信息服务 (IIS),则可能已经设置了该值(通常将其设置为虚拟目录)。如果未设置应用程序名称,则当前属性将返回空引用。
Ø ChannelServices类:该类用于注册信道,并把消息分派到信道上。
下面写一个.NET Remoting 的示例程序,示例工程的结构如下:
其中,ClassLibrary是远程对象的类库,Client、Server是控制台程序。
ClassLibrary代码如下:
using System;
namespace ClassLibrary1
{
public class Class1:MarshalByRefObject
{
public Class1()
: base()
{
Console.WriteLine("远程对象被创建!");
}
~Class1()
{
Console.WriteLine("远程对象被析构!");
}
public void SayHello(string name)
{
Console.WriteLine("你好,{0}", name);
}
public int GetSomthing(string s)
{
if (s!=null)
{
Console.WriteLine("我执行了!");
return s.Length;
}
return -1;
}
}
}
相关说明如下:
1.远程对象的创建
客户端在获取服务器端对象时,并不是获得实际的服务端对象,而是获得它的引用。因此在Remoting中,对于远程对象有一些必须的定义规范要遵循。
由于Remoting传递的对象是以引用的方式,因此所传递的远程对象类必须继承MarshalByRefObject。MSDN对MarshalByRefObject的说明是:MarshalByRefObject 是那些通过使用代理交换消息来跨越应用程序域边界进行通信的对象的基类。不是从 MarshalByRefObject 继承的对象会以隐式方式按值封送。当远程应用程序引用一个按值封送的对象时,将跨越远程处理边界传递该对象的副本。因为您希望使用代理方法而不是副本方法进行通信,因此需要继承MarshallByRefObject。
需要注意的是Class1继承自MarshalByRefObject类,即声明为远程对象。关于MarshalByRefObject具体请参考MSDN。
Server的代码如下:
using System;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting;
using ClassLibrary1;
namespace Server
{
class Program
{
/// <summary>
/// 服务端应用
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
//利用TCP通道,并侦听12345端口
TcpServerChannel channel = new TcpServerChannel(12345);
ChannelServices.RegisterChannel(channel, true);
//使用WellKnown激活方式,并且使用SingleCall模式
RemotingConfiguration.RegisterWellKnownServiceType(typeof(Class1), "Class1", WellKnownObjectMode.SingleCall);
Console.Read();
}
}
}
相关说明如下:
1、Remoting的两种通道
Remoting的通道主要有三种:Tcp和Http和IPC。在.Net中,System.Runtime.Remoting.Channel中定义了IChannel接口。IChannel接口包括了TcpChannel通道类型和Http通道类型。它们分别对应Remoting通道的前两种类型。
TcpChannel类型放在名字空间System.Runtime.Remoting.Channel.Tcp中。Tcp通道提供了基于Socket的传输工具,使用Tcp协议来跨越Remoting边界传输序列化的消息流。TcpChannel类型默认使用二进制格式序列化消息对象,因此它具有更高的传输性能。HttpChannel类型放在名字空间 System.Runtime.Remoting.Channel.Http中。它提供了一种使用Http协议,使其能在Internet上穿越防火墙传输序列化消息流。默认情况下, HttpChannel类型使用Soap格式序列化消息对象,因此它具有更好的互操作性。IPC不需要通道,适合本机使用,速度可想而知,比Tcp和Http都要快。
通常在局域网内,我们更多地使用TcpChannel;如果要穿越防火墙,则使用HttpChannel。服务端和客户端在同一台主机上,优先考虑IPC。
2、远程对象的激活方式
在访问远程类型的一个对象实例之前,必须通过一个名为Activation的进程创建它并进行初始化。这种客户端通过通道来创建远程对象,称为对象的激活。在Remoting中,远程对象的激活分为两大类:服务器端激活和客户端激活。
(1)服务器端激活,又叫做WellKnow方式,很多又翻译为知名对象。为什么称为知名对象激活模式呢?是因为服务器应用程序在激活对象实例之前会在一个众所周知的统一资源标识符(URI)上来发布这个类型。然后该服务器进程会为此类型配置一个WellKnown对象,并根据指定的端口或地址来发布对象。.Net Remoting把服务器端激活又分为SingleTon模式和SingleCall模式两种。
SingleTon模式:此为有状态模式。如果设置为SingleTon激活方式,则Remoting将为所有客户端建立同一个对象实例。当对象处于活动状态时,SingleTon实例会处理所有后来的客户端访问请求,而不管它们是同一个客户端,还是其他客户端。SingleTon实例将在方法调用中一直维持其状态。举例来说,如果一个远程对象有一个累加方法(i=0;++i),被多个客户端(例如两个)调用。如果设置为SingleTon方式,则第一个客户获得值为1,第二个客户获得值为2,因为他们获得的对象实例是相同的。如果熟悉Asp.Net的状态管理,我们可以认为它是一种Application状态。
SingleCall模式:SingleCall是一种无状态模式。一旦设置为SingleCall模式,则当客户端调用远程对象的方法时,Remoting会为每一个客户端建立一个远程对象实例,至于对象实例的销毁则是由GC自动管理的。同上一个例子而言,则访问远程对象的两个客户获得的都是1。我们仍然可以借鉴Asp.Net的状态管理,认为它是一种Session状态。
(2)客户端激活。与WellKnown模式不同,Remoting在激活每个对象实例的时候,会给每个客户端激活的类型指派一个URI。客户端激活模式一旦获得客户端的请求,将为每一个客户端都建立一个实例引用。SingleCall模式和客户端激活模式是有区别的:首先,对象实例创建的时间不一样。客户端激活方式是客户一旦发出调用的请求,就实例化;而SingleCall则是要等到调用对象方法时再创建。其次,SingleCall模式激活的对象是无状态的,对象生命期的管理是由GC管理的,而客户端激活的对象则有状态,其生命周期可自定义。其三,两种激活模式在服务器端和客户端实现的方法不一样。尤其是在客户端,SingleCall模式是由GetObject()来激活,它调用对象默认的构造函数。而客户端激活模式,则通过CreateInstance()来激活,它可以传递参数,所以可以调用自定义的构造函数来创建实例。
Client的代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels;
using ClassLibrary1;
namespace Client
{
class Program
{
static void Main(string[] args)
{
try
{
//使用TCP通道连接
TcpClientChannel channel = new TcpClientChannel();
ChannelServices.RegisterChannel(channel, true);
//获取远程对象
Class1 class1 = (Class1)Activator.GetObject(typeof(Class1), "tcp://localhost:12345/Class1");
//调用远程对象的方法
class1.SayHello("DebugLZQ");
class1.SayHello("http://www.cnblogs.com/DebugLZQ");
int i=class1.GetSomthing("DebugLZQ");
Console.WriteLine("输入的长度是,{0}", i);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message );
}
Console.Read();
}
}
}
程序的运行结果如下:
Server端运行结果:
Client端运行结果:
小结:Microsoft .NET Remoting 提供了一种允许对象通过应用程序域与另一对象进行交互的框架。也就是说,使用.NET Remoting,一个程序域可以访问另外一个程序域中的对象,就好像这个对象位于自身内部,只不过,对这个远程对象的调用,其代码是在远程应用程序域中进行的,例如在本地应用程序域中调用远程对象上一个会弹出对话框的方法,那么,这个对话框,则会在远程应用程序域中弹出。