Remoting基础
Remoting是一种分布式处理方式,是DCOM的一种升级。它改善了很多功能,并很好地融合到.Net平台下。
Microsoft® .NET Remoting 提供了一种允许对象通过应用程序域与另一对象进行交互的框架。在Windows操作系统中,是将应用程序分离为单独的进程。**这个进程形成了应用程序代码和数据周围的一道边界。**如果不采用进程间通信(RPC)机制,则在一个进程中执行的代码就不能访问另一进程。这是一种操作系统对应用程序的保护机制。然而在某些情况下,我们需要跨过应用程序域,与另外的应用程序域进行通信,即穿越边界。
首先,客户端通过remoting,访问通道channel以获得服务端对象,再通过代理解析为服务端对象。远程对象代码可以运行在服务器上(如服务器激活的对象和客户端激活的对象),然后客户端再通过remoting连接服务器,获得该服务对象,并通过序列化在客户端运行。
在remoting中,对于要传递的对象,设计者除了需要了解通道的类型和端口号之外,无需再了解数据包的格式。但需要注意的是,客户端在获取服务器端对象时,并不是直接获得实际的服务端对象,而是获得对它的引用。这既保证了客户端和服务器端有关对象的松散耦合,同时也优化了通信的性能。
remoting的两种通道
remoting的通道主要有两种:Tcp和Http。
在.Net中,System.Runtime.Remoting.Channel中定义了IChannel接口。
IChannel接口包括了TcpChannel通道类型和Http通道类型。
- TcpChannel类型放在namespace System.Runtime.Remoting.Channel.Tcp中。
Tcp通道提供了基于Socket的传输工具,使用tcp协议来跨越remoting边界,传输序列化的信息流。TcpChannel类型默认使用二进制格式序列化消息对象,因此,它具有更高的传输性能。
- HttpChannel类型放在 namespace System.Runtime.Remoting.Channel.Http中。
它提供了一种使用Http协议,使其能在Internet上穿越防火墙传输序列化信息流。默认情况下,HttpChannel类型使用Soap格式序列化消息对象,因此它具有更好的交互性。
通常在局域网内,我们更多地使用TcpChannel;
如果要穿越防火墙,则使用HttpChannel。
远程对象的激活方式
在访问远程类型的一个对象实例之前,必须通过一个名为Activation的进程,创建它并进行初始化。这种客户端通过channel来创建远程对象,称为对象的激活。
在remoting中,远程对象的激活分为两类:服务器激活和客户端激活。
- 服务器激活 【WellKnow方式】
服务器应用程序在激活对象实例之前会在一个well-known的统一资源标识符URI上来发布这个类型,然后服务器进程会为此类型配置一个wellknwon对象,并根据指定的端口或地址来发布对象。.Net Remoting把服务器激活又分为SingleTon 模式和 SingleCall模式。
-
SingleTon模式:remoting将为所有客户端建立同一个对象实例。(Application状态)
-
SingleCall模式:Remoting会为每一个客户端建立一个远程对象实例。(Session状态)
**- 客户端激活 **
客户端激活模式一旦获得客户端的请求,将为每一个客户端都建立一个实例引用。
- SingleCall模式和客户端激活模式的区别:
- 对象实例创建的时间不同:
- 客户端激活模式:客户一旦发出调用请求,就实例化。
- SingleCall模式:等到调用对象方法时再创建。 - 状态不同:
- 客户端激活模式:有状态,生命周期可自定义。
- SingleCall模式:无状态,对象生命周期由GC管理。 - 两种模式在服务器端和客户端实现的方法不一样,尤其在客户端:
- 客户端激活模式:由CreateInstance()激活,可以传递参数,因此可以调用自定义的构造函数来创建实例。
- SingleCall模式:由GetObject()激活,调用对象默认的构造函数。
远程对象的定义
前面讲到,客户端在获取服务器端对象时,并不是获得实际的服务端对象,而是获得它的引用。因此在Remoting中,对于远程对象有一些必须的定义规范要遵循。
由于Remoting传递的对象是以引用的方式,因此所传递的远程对象类必须继承MarshalByRefObject。MSDN对MarshalByRefObject的说明是:
MarshalByRefObject 是那些通过使用代理交换消息来跨越应用程序域边界进行通信的对象的基类。不是从 MarshalByRefObject 继承的对象会以隐式方式按值封送。当远程应用程序引用一个按值封送的对象时,将跨越远程处理边界传递该对象的副本。因为您希望使用代理方法而不是副本方法进行通信,因此需要继承MarshallByRefObject。
namespace dd
{
public class Class1 : MarshalByRefObject
//Remoting传递的对象是以引用的方式
{
public void Fuction1()
{
Console.WriteLine("Fuction1 accessed.");
}
public void Fuction2()
{
Console.WriteLine("Fuction2 accessed.");
}
}
}
服务器端
三步走:
- 注册通道
- 注册远程对象
- 注销通道
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Services;
namespace myServer
{
class Program
{
static void Main(string[] args)
{
//1. 注册通道
var tcpChannel = new System.Runtime.Remoting.Channels.Tcp.TcpChannel(12345);
//在实例化通道对象时,将端口号作为参数传递
ChannelServices.RegisterChannel(tcpChannel,true);
//通过RegisterChannel(通道名称)来注册该通道对象
//2. 注册远程对象
//注册了通道后,要能激活远程对象,必须在通道中注册该对象。这里采用服务器端激活方式。
System.Runtime.Remoting.RemotingConfiguration.RegisterWellKnownServiceType(
typeof(dd.Class1),//在“引用”中添加类库的ddl
"myServer001",
System.Runtime.Remoting.WellKnownObjectMode.Singleton);
Console.WriteLine("myServer001 Started!");
//3. 注销通道
ChannelServices.UnregisterChannel(tcpChannel);
}
}
}
客户端
客户端主要做两件事,
一是注册通道,Remoting中服务器端和客户端都必须通过通道来传递消息,以获得远程对象。
二是获得该远程对象的引用,然后使用远程对象。
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Services;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Activation;
using System.Runtime.Remoting.Channels.Tcp;
using dd;
namespace myClient
{
class Program
{
static void Main(string[] args)
{
//1. 注册通道
var tcpChannel = new TcpChannel();
ChannelServices.RegisterChannel(tcpChannel, true);
//2. 获得远程对象
//要获得服务器端的知名远程对象,可通过Activator进程的GetObject()方法来获得:
dd.Class1 obj = (dd.Class1)System.Activator.GetObject(
typeof(dd.Class1),
"tcp://localhost:12345/myServer001");
//调用dd中的函数,使之可以在server上执行
obj.function1();
obj.function2();
}
}
}