First we try, then we trust

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  190 随笔 :: 111 文章 :: 3519 评论 :: 358 引用

下载程序代码:http://www.cnblogs.com/Files/zhenyulu/RemotingEvent.rar


一、问题提出

.net中的Remoting技术为我们提供了一种分布式系统开发的捷径。远程对象必须继承自MarshalByRefObject,通过设置运行通道,就可以实现对远程对象的调用。我们可以选择在客户端实例化远程对象也可以选择仅仅调用远程实例化好的对象,可以选择服务器激活对象,也可以选择客户激活对象。

使用 .NET Remoting 时,必须在将应用程序部署到不同的程序集中时要多加小心。主要目标是要确保服务器上的代码不会传送到客户端。因此通常需要从远程对象中取一个接口(请参考微软的《使用 Microsoft .NET 的企业解决方案模式》)。另外,尽可能让远程对象与远程系统分离开,做到松耦合,这样也可以确保远程对象不至于做的太大,影响系统效率。

现在我们面临一个问题:就是我们的远程系统可能很庞大(比如说是一个Windows窗体)。而提供给客户调用的远程对象可能需要很小(比如说只有一个方法),如何把远程系统和远程的Remoting对象分离开,以确保远程系统的复杂度跟远程对象无关呢?显然事件(Event)是实现这种松耦合的非常好的办法。现在,我们的思路是:借助Remoting技术传递信息,再根据信息触发不同的事件,远程系统订阅这些事件并进行响应。于是远程系统就和远程对象剥离开了。

在系统实现中,SysCommon包封装了远程调用接口,确保客户端不知道服务器端对象的实现代码;Receiver对象继承自MarshalByRefObject,由客户端进行调用。同时利用delegate将调用转发给dispatcher对象;Dispatcher对象负责解释消息并根据消息内容触发不同的事件;Server对象封装了Receiver与Dispatcher,用来侦听网络调用并触发Dispatcher事件;ServerWin属于远程系统,订阅Dispatcher触发的事件并作出响应。


二、系统特点

在这里强调一下,这篇文章所讨论的Event与《C#高级编程》中的“远程调用和事件”完全是两回事。《C#高级编程》中所谓的Event是触发客户端的事件,而不是服务器端的事件,其原理是基于本地实例化远程“事件接收器”对象,而且《C#高级编程》中讲到的“远程调用和事件”也无法解决服务系统与服务对象相剥离的问题。如果对触发客户端事件感兴趣的话,可以参考Simon Robinson等著,康博译的《C#高级编程》一书第23章:带有.NET Remoting的分布式应用程序。

我这里主要讨论的是用Event将服务系统与服务对象实现相互分离。


三、系统实现

先看看系统的UML图(点击小图看大图):

其中,SysCommon包封装了远程调用接口,确保客户端不知道服务器端对象的实现代码,包括消息CallingMessage的定义以及调用接口ICommon的定义,同时将调用规范(delegate)RemotingEventHandler也放在这里了。代码如下:

using System;

namespace RemotingEvent.SysCommon
{
   [Serializable]
   
public struct CallingMessage
   
{
      
public string MsgType;      //消息类型
      public string MessageInfo;   //消息主体
   }


   
public interface ICommon
   
{
      
void ReceiveMessage(CallingMessage message);
   }


   
public delegate void RemotingEventHandler (CallingMessage message);
}

Receiver对象继承自MarshalByRefObject,由客户端进行调用,实现了ICommon接口。Receiver对象复写了InitializeLifetimeService()方法,确保当创建 Singleton 时, 第一个实例永远不会过期。Receiver包含了一个静态的符合RemotingEventHandler的方法InvokeMessageDispatcher(这是实现松耦合的关键)。这个静态delegate将由Dispatcher对象的OnDispatchHandler方法初始化(代码在Server对象中)。代码如下:

using System;
using System.Runtime.Remoting.Messaging;
using RemotingEvent.SysCommon;

namespace RemotingEvent.MessageReceiver
{
   
public class Receiver : MarshalByRefObject, ICommon
   
{

      
public static RemotingEventHandler InvokeMessageDispatcher;

      [OneWay]
      
public void ReceiveMessage(CallingMessage message)
      
{
         
if(InvokeMessageDispatcher != null)
         
{
            InvokeMessageDispatcher(message);
         }

      }


      
//=======================================================
      
// 用来确保当创建 Singleton 时, 第一个实例永远不会过期
      
//=======================================================
      public override object InitializeLifetimeService()
      
{
         
return null;
      }

   }

}

Dispatcher对象负责解释消息并根据消息内容触发不同的事件,所有事件都是在这个对象中声明的。代码如下:

using System;
using RemotingEvent.SysCommon;
using RemotingEvent.MessageReceiver;

namespace RemotingEvent.MessageDispatcher
{
   
public class Dispatcher
   
{
      
public event RemotingEventHandler BeginEvent;
      
public event RemotingEventHandler EndEvent;


      
//=========================================================
      
// 消息处理及分发主函数
      
//=========================================================
      public void OnDispatchHandler(CallingMessage message) 
      
{
         
switch(message.MsgType)
         
{
            
case "Begin":
               
if (BeginEvent != null)
                  BeginEvent(message);
               
break;
            
case "End":
               
if (EndEvent != null)
                  EndEvent(message);
               
break;
         }

      }

   }

}

Server对象主要负责将Receiver与Dispatcher整合起来构成一个有机整体。其StartServer方法用来启动服务并初始化所有对象间的关系(这也是实现松耦合的关键)。Server对象的构造函数接受一个dispatcher对象,这个dispatcher是在ServerWin服务系统中生成的,其目的为了能让ServerWin服务系统能订阅到dispatcher对象发布的事件。代码如下:

using System;
using System.Collections;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Serialization.Formatters;

using RemotingEvent.SysCommon;
using RemotingEvent.MessageDispatcher;
using RemotingEvent.MessageReceiver;

namespace RemotingEvent.LocalServer
{
   
public class Server
   
{
      
private Dispatcher dispatcher;

      
public Server(Dispatcher dispatcher)
      
{
         
this.dispatcher = dispatcher;
      }


      
public void StartServer(int port)
      
{
         
try
         
{
            BinaryServerFormatterSinkProvider serverProvider 
= new BinaryServerFormatterSinkProvider();
            BinaryClientFormatterSinkProvider clientProvider 
= new BinaryClientFormatterSinkProvider();
            serverProvider.TypeFilterLevel 
= TypeFilterLevel.Full;

            IDictionary props 
= new Hashtable();
            props[
"port"= port;
            props[
"timeout"= 2000;
            HttpChannel channel 
= new HttpChannel(props, clientProvider, serverProvider);
            ChannelServices.RegisterChannel(channel);

            RemotingConfiguration.RegisterWellKnownServiceType(
               
typeof(Receiver),
               
"Receiver.soap",
               WellKnownObjectMode.Singleton);

            Receiver.InvokeMessageDispatcher 
+= new RemotingEventHandler(dispatcher.OnDispatchHandler);
         }

         
catch
         
{
            Console.WriteLine(
"Start Server Error!");
         }

      }

   }

}

ServerWin与ClientWin是服务系统与客户端,具体代码自己下载后看吧。贴个运行结果图上来:


四、小经验

本篇文章写作时遇到一个问题,就是UML图制作问题。最开始使用Visio 2003,不过无法在其中画出Event,后来不得不使用Together 6,但是屏幕显示的东西不太漂亮,于是我便将其打印到PDF文件中,再用Acrobat 6的快照将其中的UML图放到剪贴板上,剩下的事情就是PhotoShop的事了。存盘时存储为Web格式,选择gif 16色(颜色再少就失真了)存储,便得到了现在大家看到的图片。呵呵,希望这点经验对大家有帮助。

 

posted on 2004-08-02 10:53  吕震宇  阅读(9197)  评论(15编辑  收藏