参数说明:
type:要创建的对象的类型。
args :与要调用构造函数的参数数量、顺序和类型匹配的参数数组。如果 args 为空数组或空引用(Visual Basic 中为 Nothing),则调用不带任何参数的构造函数(默认构造函数)。
activationAttributes :包含一个或多个可以参与激活的属性的数组。
这里的参数args是一个object[]数组类型。它可以传递要创建对象的构造函数中的参数。从这里其实可以得到一个结论:WellKnown激 活模式所传递的远程对象类,只能使用默认的构造函数;而Activated模式则可以用户自定义构造函数。activationAttributes参数 在这个方法中通常用来传递服务器的url。
假设我们的远程对象类ServerObject有个构造函数:
ServerObject(string pName,string pSex,int pAge)
{
name = pName;
sex = pSex;
age = pAge;
}
那么实现的代码是:
object[] attrs = {new UrlAttribute("tcp://localhost:8080/ServiceMessage")};
object[] objs = new object[3];
objs[0] = "wayfarer";
objs[1] = "male";
objs[2] = 28;
ServerRemoteObject.ServerObject = Activator.CreateInstance(
typeof(ServerRemoteObject.ServerObject),objs,attrs);
可以看到,objs[]数组传递的就是构造函数的参数。
b、public static ObjectHandle CreateInstance(string assemblyName, string typeName, object[] activationAttribute);
参数说明:
assemblyName :将在其中查找名为 typeName 的类型的程序集的名称。如果 assemblyName 为空引用(Visual Basic 中为 Nothing),则搜索正在执行的程序集。
typeName:首选类型的名称。
activationAttributes :包含一个或多个可以参与激活的属性的数组。
参数说明一目了然。注意这个方法返回值为ObjectHandle类型,因此代码与前不同:
object[] attrs = {new UrlAttribute("tcp://localhost:8080/EchoMessage")};
ObjectHandle handle = Activator.CreateInstance("ServerRemoteObject",
"ServerRemoteObject.ServerObject",attrs);
ServerRemoteObject.ServerObject obj = (ServerRemoteObject.ServerObject)handle.Unwrap();
这个方法实际上是调用的默认构造函数。ObjectHandle.Unwrap()方法是返回被包装的对象。
说明:要使用UrlAttribute,还需要在命名空间中添加:using System.Runtime.Remoting.Activation;
类 | 说明 | |
---|---|---|
IpcChannel | 提供使用 IPC 协议传输消息的信道实现。 | |
IpcClientChannel | 为远程调用实现使用 IPC 协议传输消息的客户端信道。 | |
IpcServerChannel | 为远程调用实现使用 IPC 系统传输消息的服务器信道。 |
这个时候,我们必须用到System.Collection中的IDictionary接口:
注册Tcp通道:
IDictionary tcpProp = new Hashtable();
tcpProp["name"] = "tcp9090";
tcpProp["port"] = 9090;
IChannel channel = new TcpChannel(tcpProp,
new BinaryClientFormatterSinkProvider(),
new BinaryServerFormatterSinkProvider());
ChannelServices.RegisterChannel(channel);
注册Http通道:
IDictionary httpProp = new Hashtable();
httpProp["name"] = "http8080";
httpProp["port"] = 8080;
IChannel channel = new HttpChannel(httpProp,
new SoapClientFormatterSinkProvider(),
new SoapServerFormatterSinkProvider());
ChannelServices.RegisterChannel(channel);
在name属性中,定义不同的通道名称就可以了。
2、远程对象元数据相关性
由于服务器端和客户端都要用到远程对象,通常的方式是生成两份完全相同的对象Dll,分别添加引用。不过为了代码的安全性,且降低客户端对远程对象元数据的相关性,我们有必要对这种方式进行改动。即在服务器端实现远程对象,而在客户端则删除这些实现的元数据。
由于激活模式的不同,在客户端创建对象的方法也不同,所以要分离元数据的相关性,也应分为两种情况。
(1) WellKnown激活模式:
通过接口来实现。在服务器端,提供接口和具体类的实现,而在客户端仅提供接口:
public interface IServerObject
{
Person GetPersonInfo(string name,string sex,int age);
}
public class ServerObject:MarshalByRefObject,IServerObject
{ ......}
注意:两边生成该对象程序集的名字必须相同,严格地说,是命名空间的名字必须相同。
(2) 客户端激活模式:
如前所述,对于客户端激活模式,不管是使用静态方法,还是使用CreateInstance()方法,都必须在客户 端调用构造函数实例化对象。所 以,在客户端我们提供的远程对象,就不能只提供接口,而没有类的实现。实际上,要做到与远程对象元数据的分离,可以由两种方法供选择:
a、利用WellKnown激活模式模拟客户端激活模式:
方法是利用设计模式中的“抽象工厂
我们在服务器端的远程对象中加上抽象工厂的接口和实现类:
public interface IServerObject
{
Person GetPersonInfo(string name,string sex,int age);
}
public interface IServerObjFactory
{
IServerObject CreateInstance();
}
public class ServerObject:MarshalByRefObject,IServerObject
{
public Person GetPersonInfo(string name,string sex,int age)
{
Person person = new Person();
person.Name = name;
person.Sex = sex;
person.Age = age;
return person;
}
}
public class ServerObjFactory:MarshalByRefObject,IServerObjFactory
{
public IServerObject CreateInstance()
{
return new ServerObject();
}
}
然后再客户端的远程对象中只提供工厂接口和原来的对象接口:
public interface IServerObject
{
Person GetPersonInfo(string name,string sex,int age);
}
public interface IServerObjFactory
{
IServerObject CreateInstance();
}
我们用WellKnown激活模式注册远程对象,在服务器端:
//传递对象;
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(ServerRemoteObject.ServerObjFactory),
"ServiceMessage",WellKnownObjectMode.SingleCall);
注意这里注册的不是ServerObject类对象,而是ServerObjFactory类对象。
客户端:
ServerRemoteObject.IServerObjFactory serverFactory =
(ServerRemoteObject.IServerObjFactory) Activator.GetObject(
typeof(ServerRemoteObject.IServerObjFactory),
"tcp://localhost:8080/ServiceMessage");
ServerRemoteObject.IServerObject serverObj = serverFactory.CreateInstance();
为什么说这是一种客户端激活模式的模拟呢?从激活的方法来看,我们是使用了SingleCall模式来激活对象,但 此时激活的并非我们要传递的远程 对象,而是工厂对象。如果客户端要创建远程对象,还应该通过工厂对象的CreateInstance()方法来获得。而这个方法正是在客户端调用的。因此 它的实现方式就等同于客户端激活模式。
b、利用替代类来取代远程对象的元数据
实际上,我们可以用一个trick,来欺骗Remoting。这里所说的替代类就是这个trick了。既然是提供服 务,Remoting传递的远程 对象其实现的细节当然是放在服务器端。而要在客户端放对象的副本,不过是因为客户端必须调用构造函数,而采取的无奈之举。既然具体的实现是在服务器端,又 为了能在客户端实例化,那么在客户端就实现这些好了。至于实现的细节,就不用管了。
如果远程对象有方法,服务器端则提供方法实现,而客户端就提供这个方法就OK了,至于里面的实现,你可以是抛出一个 异常,或者return 一个null值;如果方法返回void,那么里面可以是空。关键是这个客户端类对象要有这个方法。这个方法的实现,其实和方法的声明差不多,所以我说是一 个trick。方法如是,构造函数也如此。
还是用代码来说明这种“阴谋”,更直观:
服务器端:
public class ServerObject:MarshalByRefObject
{
public ServerObject()
{
}
public Person GetPersonInfo(string name,string sex,int age)
{
Person person = new Person();
person.Name = name;
person.Sex = sex;
person.Age = age;
return person;
}
}
客户端:
public class ServerObject:MarshalByRefObject
{
public ServerObj()
{
throw new System.NotImplementedException();
}
public Person GetPersonInfo(string name,string sex,int age)
{
throw new System.NotImplementedException();
}
}
比较客户端和服务器端,客户端的方法GetPersonInfo(),没有具体的实现细节,只是抛出了一个异常。或者直接写上语句return null,照样OK。我们称客户端的这个类为远程对象的替代类。
3、利用配置文件实现
前面所述的方法,于服务器uri、端口、以及激活模式的设置是用代码来完成的。其实我们也可以用配置文件来设置。这样做有个好处,因为这个配置文件是Xml文档。如果需要改变端口或其他,我们就不需要修改程序,并重新编译,而是只需要改变这个配置文件即可。
(1) 服务器端的配置文件:
<configuration>
<system.runtime.remoting>
<application name="ServerRemoting">
<service>
<wellknown mode="Singleton" type="ServerRemoteObject.ServerObject" objectUri="ServiceMessage"/>
</service>
<channels>
<channel ref="tcp" port="8080"/>
</channels>
</application>
</system.runtime.remoting>
</configuration>
如果是客户端激活模式,则把wellknown改为activated,同时删除mode属性。
把该配置文件放到服务器程序的应用程序文件夹中,命名为ServerRemoting.config。那么前面的服务器端程序直接用这条语句即可:
RemotingConfiguration.Configure("ServerRemoting.config");
(2) 客户端配置文件
如果是客户端激活模式,修改和上面一样。调用也是使用RemotingConfiguration.Configure()方法来调用存储在客户端的配置文件。
配置文件还可以放在machine.config中。如果客户端程序是web应用程序,则可以放在web.config中。
4、启动/关闭指定远程对象
Remoting中没有提供类似UnregisterWellKnownServiceType()的方法,也即是 说,一旦通过注册了远程对象,如 果没有关闭通道的话,该对象就一直存在于通道中。只要客户端激活该对象,就会创建对象实例。如果Remoting传送的只有一个远程对象,这不存在问题, 关闭通道就可以了。如果传送多个远程对象呢?要关闭指定的远程对象应该怎么做?关闭之后又需要启动又该如何?
我们注意到在Remoting中提供了Marshal()和Disconnect()方法,答案就在这里。 Marshal()方法是将 MarshalByRefObject类对象转化为ObjRef类对象,这个对象是存储生成代理以与远程对象通讯所需的所有相关信息。这样就可以将该实例 序列化以便在应用程序域之间以及通过网络进行传输,客户端就可以调用了。而Disconnect()方法则将具体的实例对象从通道中断开。
方法如下:
首先注册通道:
TcpChannel channel = new TcpChannel(8080);
ChannelServices.RegisterChannel(channel);
接着启动服务:
先在服务器端实例化远程对象。
ServerObject obj = new ServerObject();
然后,注册该对象。注意这里不用RemotingConfiguration.RegisterWellKnownServiceType(),而是使用RemotingServices.Marshal():
ObjRef objrefWellKnown = RemotingServices.Marshal(obj, "ServiceMessage");
如果要注销对象,则:
RemotingServices.Disconnect(obj);
要注意,这里Disconnect的类对象必须是前面实例化的对象。正因为此,我们可以根据需要创建指定的远程对象,而关闭时,则Disconnect之前实例化的对象。
至于客户端的调用,和前面WellKnown模式的方法相同,仍然是通过 Activator.GetObject()来获得。但从实现代码来看,我 们会注意到一个问题,由于服务器端是显式的实例化了远程对象,因此不管客户端有多少,是否相同,它们调用的都是同一个远程对象。因此我们将这个方法称为模 拟的SingleTon模式。
客户端激活模式
我们也可以通过Marshal()和Disconnect()来模拟客户端激活模式。首先我们来回顾“远程对象元数 据相关性”一节,在这一节中,我 说到采用设计模式的“抽象工厂”来创建对象实例,以此用SingleCall模式来模拟客户端激活模式。在仔细想想前面的模拟的SingleTon模式。 是不是答案就将呼之欲出呢?
在“模拟的SingleTon”模式中,我们是将具体的远程对象实例进行Marshal,以此让客户端获得该对象的 引用信息。那么我们换一种思路, 当我们用抽象工厂提供接口,工厂类实现创建远程对象的方法。然后我们在服务器端创建工厂类实例。再将这个工厂类实例进行Marshal。而客户端获取对象 时,不是获取具体的远程对象,而是获取具体的工厂类对象。然后再调用CreateInstance()方法来创建具体的远程对象实例。此时,对于多个客户 端而言,调用的是同一个工厂类对象;然而远程对象是在各个客户端自己创建的,因此对于远程对象而言,则是由客户端激活,创建的是不同对象了。
当我们要启动/关闭指定对象时,只需要用Disconnet()方法来注销工厂类对象就可以了。
http://hi.baidu.com/yx232/blog/item/001e6773c5ea89108601b05a.html