NET Remoting程序开发入门篇文档格式.docx
《NET Remoting程序开发入门篇文档格式.docx》由会员分享,可在线阅读,更多相关《NET Remoting程序开发入门篇文档格式.docx(27页珍藏版)》请在冰豆网上搜索。
Tcp通道提供了基于Socket的传输工具,使用Tcp协议来跨越Remoting边界传输序列化的消息流。
TcpChannel类型默认使用二进制格式序列化消息对象,因此它具有更高的传输性能。
HttpChannel类型放在名字空间System.Runtime.Remoting.Channel.Http中。
它提供了一种使用Http协议,使其能在Internet上穿越防火墙传输序列化消息流。
默认情况下,HttpChannel类型使用Soap格式序列化消息对象,因此它具有更好的互操作性。
通常在局域网内,我们更多地使用TcpChannel;
如果要穿越防火墙,则使用HttpChannel。
2、远程对象的激活方式
在访问远程类型的一个对象实例之前,必须通过一个名为Activation的进程创建它并进行初始化。
这种客户端通过通道来创建远程对象,称为对象的激活。
在Remoting中,远程对象的激活分为两大类:
服务器端激活和客户端激活。
(1)服务器端激活,又叫做WellKnow方式,很多又翻译为知名对象。
为什么称为知名对象激活模式呢?
是因为服务器应用程序在激活对象实例之前会在一个众所周知的统一资源标识符(URI)上来发布这个类型。
然后该服务器进程会为此类型配置一个WellKnown对象,并根据指定的端口或地址来发布对象。
.NetRemoting把服务器端激活又分为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()来激活,它可以传递参数,所以可以调用自定义的构造函数来创建实例。
二、远程对象的定义
前面讲到,客户端在获取服务器端对象时,并不是获得实际的服务端对象,而是获得它的引用。
因此在Remoting中,对于远程对象有一些必须的定义规范要遵循。
由于Remoting传递的对象是以引用的方式,因此所传递的远程对象类必须继承MarshalByRefObject。
MSDN对MarshalByRefObject的说明是:
MarshalByRefObject是那些通过使用代理交换消息来跨越应用程序域边界进行通信的对象的基类。
不是从MarshalByRefObject继承的对象会以隐式方式按值封送。
当远程应用程序引用一个按值封送的对象时,将跨越远程处理边界传递该对象的副本。
因为您希望使用代理方法而不是副本方法进行通信,因此需要继承MarshallByRefObject。
以下是一个远程对象类的定义:
publicclassServerObject:
MarshalByRefObject
{
publicPersonGetPersonInfo(stringname,stringsex,intage)
{
Personperson=newPerson();
person.Name=name;
person.Sex=sex;
person.Age=age;
returnperson;
}
}
这个类只实现了最简单的方法,就是设置一个人的基本信息,并返回一个Person类对象。
注意这里返回的Person类。
由于这里所传递的Person则是以传值的方式来完成的,而Remoting要求必须是引用的对象,所以必须将Person类序列化。
因此,在Remoting中的远程对象中,如果还要调用或传递某个对象,例如类,或者结构,则该类或结构则必须实现串行化Attribute[SerializableAttribute]:
[Serializable]
publicclassPerson
publicPerson()
{}
privatestringname;
privatestringsex;
privateintage;
publicstringName
get{returnname;
set{name=value;
publicstringSex
get{returnsex;
set{sex=value;
publicintAge
get{returnage;
set{age=value;
将该远程对象以类库的方式编译成Dll。
这个Dll将分别放在服务器端和客户端,以添加引用。
在Remoting中能够传递的远程对象可以是各种类型,包括复杂的DataSet对象,只要它能够被序列化。
远程对象也可以包含事件,但服务器端对于事件的处理比较特殊,我将在本系列之三中介绍。
三、服务器端
根据第一部分所述,根据激活模式的不同,通道类型的不同服务器端的实现方式也有所不同。
大体上说,服务器端应分为三步:
1、注册通道
要跨越应用程序域进行通信,必须实现通道。
如前所述,Remoting提供了IChannel接口,分别包含TcpChannel和HttpChannel两种类型的通道。
这两种类型除了性能和序列化数据的格式不同外,实现的方式完全一致,因此下面我们就以TcpChannel为例。
注册TcpChannel,首先要在项目中添加引用“System.Runtime.Remoting”,然后using名字空间:
System.Runtime.Remoting.Channel.Tcp。
代码如下:
TcpChannelchannel=newTcpChannel(8080);
ChannelServices.ReGISterChannel(channel);
在实例化通道对象时,将端口号作为参数传递。
然后再调用静态方法RegisterChannel()来注册该通道对象即可。
2、注册远程对象
注册了通道后,要能激活远程对象,必须在通道中注册该对象。
根据激活模式的不同,注册对象的方法也不同。
(1)SingleTon模式
对于WellKnown对象,可以通过静态方法RemotingConfiguration.RegisterWellKnownServiceType()来实现:
RemotingConfiguration.RegisterWellKnownServiceType(typeof(ServerRemoteObject.ServerObject),
"
ServiceMessage"
WellKnownObjectMode.SingleTon);
(2)SingleCall模式
注册对象的方法基本上和SingleTon模式相同,只需要将枚举参数WellKnownObjectMode改为SingleCall就可以了。
WellKnownObjectMode.SingleCall);
(3)客户端激活模式
对于客户端激活模式,使用的方法又有不同,但区别不大,看了代码就一目了然。
RemotingConfiguration.ApplicationName="
;
RemotingConfiguration.RegisterActivatedServiceType(typeof(ServerRemoteObject.ServerObject));
为什么要在注册对象方法前设置ApplicationName属性呢?
其实这个属性就是该对象的URI。
对于WellKnown模式,URI是放在RegisterWellKnownServiceType()方法的参数中,当然也可以拿出来专门对ApplicationName属性赋值。
而RegisterActivatedServiceType()方法的重载中,没有ApplicationName的参数,所以必须分开。
3、注销通道
如果要关闭Remoting的服务,则需要注销通道,也可以关闭对通道的监听。
在Remoting中当我们注册通道的时候,就自动开启了通道的监听。
而如果关闭了对通道的监听,则该通道就无法接受客户端的请求,但通道仍然存在,如果你想再一次注册该通道,会抛出异常。
//获得当前已注册的通道;
IChannel[]channels=ChannelServices.RegisteredChannels;
//关闭指定名为MyTcp的通道;
foreach(IChanneleachChannelinchannels)
if(eachChannel.ChannelName=="
MyTcp"
)
TcpChanneltcpChannel=(TcpChannel)eachChannel;
//关闭监听;
tcpChannel.StopListening(null);
//注销通道;
ChannelServices.UnregisterChannel(tcpChannel);
代码中,RegisterdChannel属性获得的是当前已注册的通道。
在Remoting中,是允许同时注册多个通道的,这一点会在后面说明。
四、客户端
客户端主要做两件事,一是注册通道。
这一点从图一就可以看出,Remoting中服务器端和客户端都必须通过通道来传递消息,以获得远程对象。
第二步则是获得该远程对象。
1、注册通道:
TcpChannelchannel=newTcpChannel();
注意在客户端实例化通道时,是调用的默认构造函数,即没有传递端口号。
事实上,这个端口号是缺一不可的,只不过它的指定被放在后面作为了Uri的一部分。
2、获得远程对象。
与服务器端相同,不同的激活模式决定了客户端的实现方式也将不同。
不过这个区别仅仅是WellKnown激活模式和客户端激活模式之间的区别,而对于SingleTon和SingleCall模式,客户端的实现完全相同。
(1)WellKnown激活模式
要获得服务器端的知名远程对象,可通过Activator进程的GetObject()方法来获得:
ServerRemoteObject.ServerObjectserverObj=(ServerRemoteObject.ServerObject)Activator.GetObject(
typeof(ServerRemoteObject.ServerObject),"
tcp:
//localhost:
8080/ServiceMessage"
);
首先以WellKnown模式激活,客户端获得对象的方法是使用GetObject()。
其中参数第一个是远程对象的类型。
第二个参数就是服务器端的uri。
如果是http通道,自然是用http:
8080/ServiceMessage了。
因为我是用本地机,所以这里是localhost,你可以用具体的服务器IP地址来代替它。
端口必须和服务器端的端口一致。
后面则是服务器定义的远程对象服务名,即ApplicationName属性的内容。
(2)客户端激活模式
如前所述,WellKnown模式在客户端创建对象时,只能调用默认的构造函数,上面的代码就说明了这一点,因为GetObject()方法不能传递构造函数的参数。
而客户端激活模式则可以通过自定义的构造函数来创建远程对象。
客户端激活模式有两种方法:
1)调用RemotingConfiguration的静态方法RegisterActivatedClientType()。
这个方法返回值为Void,它只是将远程对象注册在客户端而已。
具体的实例化还需要调用对象类的构造函数。
RemotingConfiguration.RegisterActivatedClientType(typeof(ServerRemoteObject.ServerObject),
ServerRemoteObject.ServerObjectserverObj=newServerRemoteObject.ServerObject();
2)调用进程Activator的CreateInstance()方法。
这个方法将创建方法参数指定类型的类对象。
它与前面的GetObject()不同的是,它要在客户端调用构造函数,而GetObject()只是获得对象,而创建实例是在服务器端完成的。
CreateInstance()方法有很多个重载,我着重说一下其中常用的两个。
a、publicstaticobjectCreateInstance(Typetype,object[]args,object[]activationAttributes);
参数说明:
type:
要创建的对象的类型。
args:
与要调用构造函数的参数数量、顺序和类型匹配的参数数组。
如果args为空数组或空引用(VisualBasic中为Nothing),则调用不带任何参数的构造函数(默认构造函数)。
activationAttributes:
包含一个或多个可以参与激活的属性的数组。
这里的参数args是一个object[]数组类型。
它可以传递要创建对象的构造函数中的参数。
从这里其实可以得到一个结论:
WellKnown激活模式所传递的远程对象类,只能使用默认的构造函数;
而Activated模式则可以用户自定义构造函数。
activationAttributes参数在这个方法中通常用来传递服务器的url。
假设我们的远程对象类ServerObject有个构造函数:
ServerObject(stringpName,stringpSex,intpAge)
name=pName;
sex=pSex;
age=pAge;
那么实现的代码是:
object[]attrs={newUrlAttribute("
)};
object[]objs=newobject[3];
objs[0]="
wayfarer"
objs[1]="
male"
objs[2]=28;
ServerRemoteObject.ServerObject=Activator.CreateInstance(
typeof(ServerRemoteObject.ServerObject),objs,attrs);
可以看到,objs[]数组传递的就是构造函数的参数。
b、publicstaticObjectHandleCreateInstance(stringassemblyName,stringtypeName,object[]activationAttribute);
assemblyName:
将在其中查找名为typeName的类型的程序集的名称。
如果assemblyName为空引用(VisualBasic中为Nothing),则搜索正在执行的程序集。
typeName:
首选类型的名称。
参数说明一目了然。
注意这个方法返回值为ObjectHandle类型,因此代码与前不同:
8080/EchoMessage"
ObjectHandlehandle=Activator.CreateInstance("
ServerRemoteObject"
ServerRemoteObject.ServerObject"
attrs);
ServerRemoteObject.ServerObjectobj=(ServerRemoteObject.ServerObject)handle.Unwrap();
这个方法实际上是调用的默认构造函数。
ObjectHandle.Unwrap()方法是返回被包装的对象。
说明:
要使用UrlAttribute,还需要在命名空间中添加:
usingSystem.Runtime.Remoting.Activation;
五、Remoting基础的补充
通过上面的描述,基本上已经完成了一个最简单的Remoting程序。
这是一个标准的创建Remoting程序的方法,但在实际开发过程中,我们遇到的情况也许千奇百怪,如果只掌握一种所谓的“标准”,就妄想可以“一招鲜、吃遍天”,是不可能的。
1、注册多个通道
在Remoting中,允许同时创建多个通道,即根据不同的端口创建不同的通道。
但是,Remoting要求通道的名字必须不同,因为它要用来作为通道的唯一标识符。
虽然IChannel有ChannelName属性,但这个属性是只读的。
因此前面所述的创建通道的方法无法实现同时注册多个通道的要求。
这个时候,我们必须用到System.Collection中的IDictionary接口:
注册Tcp通道:
IDictionarytcpProp=newHashtable();
tcpProp["
name"
]="
tcp9090"
port"
]=9090;
IChannelchannel=newTcpChannel(tcpProp,
newBinaryClientFormatterSinkProvider(),
newBinaryServerFormatterSinkProvider());
注册Http通道:
IDictionaryhttpProp=newHashtable();
httpProp["
http8080"
]=8080;
IChannelchannel=newHttpChannel(httpProp,
newSoapClientFormatterSinkProvider(),
newSoapServerFormatterSinkProvider());
ChannelServices.RegisterChannel(channel);
在name