分类广告


推荐文章

  • 没有找到任何内容!
您当前的位置:中国站长下载网络编程NET专区 → 文章内容

Microsoft .Net Remoting系列专题之三:Remoting事件处理全接触4

  • 作者:佚名    来源:不详    发布时间:2007-6-30 10:22:13
  • 字体大小:
     我放了一个ListBox控件来显示一些信息,例如显示服务器启动了。而BroadCast按钮就是广播消息的,单击该按钮,会弹出一个对话框:


  
  BraodCast按钮的代码:
  private void btnBC_Click(object sender, System.EventArgs e)
  {
   BroadCastForm bcForm = new BroadCastForm();
   bcForm.StartPosition = FormStartPosition.CenterParent;
   bcForm.ShowDialog();
  }
  
  在对话框中,最主要的就是Send按钮:
  if (txtInfo.Text != string.Empty)
  {
   ServerForm.Obj.BroadCastingInfo(txtInfo.Text);
  }
  else
  {
   MessageBox.Show("请输入信息!");
  }
  但是很简单,就是调用远程对象的发送消息方法而已。
  
  现在该实现客户端了。我们可以参照前面的例子,只是把服务端改为客户端而已。另外考虑到序列化安全级别的问题,所以代码会是这样:
  private void ClientForm_Load(object sender, System.EventArgs e)
  {
   BinaryServerFormatterSinkProvider serverProvider = new
   BinaryServerFormatterSinkProvider();
   BinaryClientFormatterSinkProvider clientProvider = new
   BinaryClientFormatterSinkProvider();
   serverProvider.TypeFilterLevel = TypeFilterLevel.Full;
  
   IDictionary props = new Hashtable();
   props["port"] = 0;
   HttpChannel channel = new HttpChannel(props,clientProvider,serverProvider);
   ChannelServices.RegisterChannel(channel);
  
   watch = (IBroadCast)Activator.GetObject(
   typeof(IBroadCast),"http://localhost:8080/BroadCastMessage.soap");
   watch.BroadCastEvent += new BroadCastEventHandler(BroadCastingMessage);
  }
  注意客户端通道的端口号应设置为0,这表示客户端自动选择可用的端口号。如果要设置为指定的端口号,则必须保证与服务端通道的端口号不相同。
  然后是,BroadCastEventHandler委托的方法:
  public void BroadCastingMessage(string message)
  {
   txtMessage.Text += "I got it:" + message;
   txtMessage.Text += System.Environment.NewLine;
  }
  客户端界面如图:
  
  好,下面让我们满怀期盼,来运行这段程序。首先启动服务端应用程序,然后启动客户端。哎呀,糟糕,居然出现了错误信息!
  
  “人之不如意事,十常居八九。”不用沮丧,让我们分析原因。首先看看错误信息,它报告我们没有找到Client程序集。然而事实上,Client程序集当然是有的。那么再来调试一下,是哪一步出现的问题呢?设置好断点,进行逐语句跟踪。前面注册通道一切正常,当运行到watch.BroadCastEvent += new BroadCastEventHandler(BroadCastingMessage)语句时,错误出现了!
  
  也就是说,远程对象的创建是成功的,但在订阅事件的时候失败了。原因是什么呢?原来,客户端的委托是通过序列化后获得的,在订阅事件的时候,委托试图装载包含与签名相同的方法的程序集,也就是BroadCastingMessage方法所在的程序集Client。然而这个装载的过程发生在服务端,而在服务端,并没有Client程序集存在,自然就发生了上面的异常。
  
  原因清楚了,怎么解决?首先BroadCastingMessage方法肯定是在客户端中,所以不可避免,委托装载Client程序集的过程也必须在客户端完成。而服务端事件又是由远程对象来捕获的,因此,在客户端注册的也就必须是远程对象事件了。一个要求必须在客户端,一个又要求必须在服务端,事情出现了自相矛盾的地方。
  
  那么,让我们先想想这样一个例子。假设我们要交换x和y的值,该这样完成?很简单,引入一个中间变量就可以了。
  int x=1,y=2,z;
  z = x;
  x = y;
  y = z;
  这个游戏相信大家都会玩吧,那么好的,我们也需要引入这样一个“中间”对象。这个中间对象和原来的远程对象在事件处理方面,代码完全一致:
  public class EventWrapper:MarshalByRefObject
  {
   public event BroadCastEventHandler LocalBroadCastEvent;
  
   //[OneWay]
   public void BroadCasting(string message)
   {
   LocalBroadCastEvent(message);
   }
  
   public override object InitializeLifetimeService()
   {
   return null;
   }
  }
  
  不过不同之处在于:这个Wrapper类必须在客户端和服务端上都要部署,所以,这个类应该放在公共程序集Common.dll中。
  
  现在再来修改原来的客户端代码:
  watch = (IBroadCast)Activator.GetObject(
   typeof(IBroadCast),"http://localhost:8080/BroadCastMessage.soap");
  watch.BroadCastEvent += new BroadCastEventHandler(BroadCastingMessage);
  修改为:
  watch = (IBroadCast)Activator.GetObject(
   typeof(IBroadCast),"http://localhost:8080/BroadCastMessage.soap");
  EventWrapper wrapper = new EventWrapper();
  wrapper.LocalBroadCastEvent += new BroadCastEventHandler(BroadCastingMessage);
  watch.BroadCastEvent += new BroadCastEventHandler(wrapper.BroadCasting);
  
  为什么这样做就可以了呢?也许画一幅图就很容易说明,可惜我的艺术天分实在很糟糕,我希望以后可以改进这一点。还是用文字来说明吧。
  
  前面说,委托要装载client程序集。现在我们把远程对象委托装载的权利移交给EventWrapper。因为这个类对象是放在客户端的,所以它要装载client程序集丝毫没有问题。语句:
  EventWrapper wrapper = new EventWrapper();
  wrapper.LocalBroadCastEvent += new BroadCastEventHandler(BroadCastingMessage);
  实现了这个功能。
  
  不过此时虽然订阅了事件,但事件还是客户端的,没有与服务端联系起来。而服务端的事件是放到远程对象中的,所以,还要订阅事件,这个任务由远程对象watch来完成。但此时它订阅的不再是BroadCastingMessage了,而是EventWrapper的触发事件方法BroadCasting。那么此时委托同样要装载程序集,但此时装载的就是BroadCasting所在的程序集了。由于装载发生的地点是在服务端。呵呵,高兴的是,BroadCasting所在的程序集正是公共程序集(前面已说过,EventWrapper应放到公共程序集Common.dll中),而公共程序集在服务端和客户端都已经部署了。自然就不会出现找不到程序集的问题了。