.NET Remoting程序开发入门篇(四)

五、Remoting基础的补充

  通过上面的描述,基本上已经完成了一个最简单的Remoting程序。这是一个标准的创建Remoting程序的方法,但在实际开发过程中,我们遇到的情况也许千奇百怪,如果只掌握一种所谓的“标准”,就妄想可以“一招鲜、吃遍天”,是不可能的。

  1、注册多个通道

  在Remoting中,允许同时创建多个通道,即根据不同的端口创建不同的通道。但是,Remoting要求通道的名字必须不同,因为它要用来作为通道的唯一标识符。虽然IChannel有ChannelName属性,但这个属性是只读的。因此前面所述的创建通道的方法无法实现同时注册多个通道的要求。

  这个时候,我们必须用到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;
}
}

posted @ 2007-07-05 19:44  PointNet  阅读(772)  评论(0编辑  收藏  举报