您的位置:首页 > 其它

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

2011-04-01 15:34 253 查看
前言:在Remoting中处理事件其实并不复杂,但其中有些技巧需要你去挖掘出来。正是这些技巧,仿佛森严的壁垒,让许多人望而生畏,或者是不知所谓,最后放弃了事件在Remoting的使用。关于这个主题,在网上也有很多讨论,相关的技术文章也不少,遗憾的是,很多文章概述的都不太全面。我在研究Remoting的时候,也对事件处理发生了兴趣。经过参考相关的书籍、文档,并经过反复的试验,深信自己能够把这个问题阐述清楚了。
本文对于Remoting和事件的基础知识不再介绍,有兴趣的可以看我的系列文章,或查阅相关的技术文档。

本文示例代码下载:

Remoting事件(客户端发传真)

Remoting事件(服务端广播)

Remoting事件(服务端广播改进)

应用Remoting技术的分布式处理程序,通常包括三部分:远程对象、服务端、客户端。因此从事件的方向上看,就应该有三种形式:
1、服务端订阅客户端事件
2、客户端订阅服务端事件
3、客户端订阅客户端事件

服务端订阅客户端事件,即由客户端发送消息,服务端捕捉该消息,然后响应该事件,相当于下级向上级发传真。反过来,客户端订阅服务端事件,则是由服务端发送消息,此时,所有客户端均捕获该消息,激发事件,相当于是一个系统广播。而客户端订阅客户端事件呢?就类似于聊天了。由某个客户端发出消息,其他客户端捕获该消息,激发事件。可惜的是,我并没有找到私聊的解决办法。当客户端发出消息后,只要订阅了该事件的,都会获得该信息。

然而不管是哪一种方式,究其实质,真正包含事件的还是远程对象。原理很简单,我们想一想,在Remoting中,客户端和服务端传递的内容是什么呢?毋庸置疑,是远程对象。因此,我们传递的事件消息,自然是被远程对象所包裹。这就像EMS快递,远程对象是运送信件的汽车,而事件消息就是汽车所装载的信件。至于事件传递的方向,只是发送者和订阅者的角色发生了改变而已。

一、 服务端订阅客户端事件
服务端订阅客户端事件,相对比较简单。我们就以发传真为例。首先,我们必须具备传真机和要传真的文件,这就好比我们的远程对象。而且这个传真机上必须具备“发送”的操作按钮。这就好比是远程对象中的一个委托。当客户发送传真时,就需要在客户端上激活一个发送消息的方法,这就好比我们按了“发送”按钮。消息发送到服务端后,触发事件,这个事件正是服务端订阅的。服务端获得该事件消息后,再处理相关业务。这就好比接收传真的人员,当传真收到后,会听到接通的声音,此时选择“接收”后,该消息就被捕获了。

现在,我们就来模拟这个流程。首先定义远程对象,这个对象处理的应该是一个发送传真的业务:
首先是远程对象的公共接口(Common.dll):

public delegate void FaxEventHandler(string fax);
public interface IFaxBusiness
{
void SendFax(string fax);
}


注意,在公共接口程序集中,定义了一个公共委托。

然后我们定义具体处理传真业务的远程对象类(FaxBusiness.dll),在这个类中,先要添加对公共接口程序集的引用:

public class FaxBusiness:MarshalByRefObject,IFaxBusiness
{
public static event FaxEventHandler FaxSendedEvent;

#region

public void SendFax(string fax)
{
if (FaxSendedEvent != null)
{
FaxSendedEvent(fax);
}
}

#endregion

public override object InitializeLifetimeService()
{
return null;
}
}


这个远程对象中,事件的类型就是我们在公共程序集Common.dll中定义的委托类型。SendFax实现了接口IFaxBusiness中的方法。这个方法的签名和定义的委托一致,它调用了事件FaxSendedEvent。
特殊的地方是我们定义的远程对象最好是重写MarshalByRefObject类的InitializeLifetimeService()方法。返回null值表明这个远程对象的生命周期为无限大。为什么要重写该方法呢?道理不言自明,如果生命周期不进行限制的话,一旦远程对象的生命周期结束,事件就无法激活了。
接下来就是分别实现客户端和服务端了。服务端是一个Windows应用程序,界面如下:

3、最后的改进

使用OneWay固然可以解决上述的问题,但不够友好。因为对于广播消息的一方来说,象被蒙上了眼睛一样,对于客户端发生的事情懵然不知。这并不是一个好的idea。在Ingo Rammer的Advanced .NET Remoting一书中,Ingo Rammer先生提出了一个更好的办法,就是在发送信息一方时,检查了委托链。并在委托链的遍历中来捕获异常。当其中一个委托发生异常时,显示提示信息。然后继续遍历后面的委托,这样既保证了异常信息的提示,又保证了其他订阅者正常接收消息。因此,我对本例的远程对象进行了修改,注释掉[OneWay],修改了BroadCastInfo()方法:

//[OneWay]
public void BroadCastingInfo(string info)
{
if (BroadCastEvent != null)
{
BroadCastEventHandler tempEvent = null;
int index = 1; //记录事件订阅者委托的索引,为方便标识,从1开始。
foreach (Delegate del in BroadCastEvent.GetInvocationList())
{
try
{
tempEvent = (BroadCastEventHandler)del;
tempEvent(info);
}
catch
{
MessageBox.Show("事件订阅者" + index.ToString() + "发生错误,系统将取消事件订阅!");
BroadCastEvent -= tempEvent;
}
index++;
}
}
else
{
MessageBox.Show("事件未被订阅或订阅发生错误!");
}
}


我们来试验一下。首先打开服务端,然后同时打开三个客户端。广播消息:



消息发送正常。

接着关闭其中一个客户端窗口,再广播消息(注意为模拟客户端异常情况,应在ClientForm_Closing方法中把第一步改进的取消订阅代码注释。否则不会发生异常。难道你真的愿意用断电来导致异常发生吗^_^),结果如图:



此时服务端报告了“事件订阅者1发生错误,系统将取消事件订阅”。注意此时另外两个客户端,还是和前面一样,只有两条广播信息。

当我们点击提示框的“确定”按钮后,广播仍然发送:



通过这样的改进后,程序更加的完善,也更加的健壮和友好!

附:
示例代码说明:
1、 Remoting事件(客户端发传真)压缩包:为第一节内容;
2、 Remoting事件(服务端广播)压缩包:为第二节、第三节内容,其中:
第二节代码包含于:
#region 客户端订阅服务端事件
#endregion
第三节代码包含于:
#region 客户端订阅客户端事件
#endregion
如果要实现第二节的程序,请注释掉第三节代码;反之亦然。示例程序默认为第二节程序。
3、 运行示例程序时,请先运行服务端程序,然后运行客户端程序。否则会抛出“基础连接已关闭”的异常。
4、 解决方案均放在Common(或ICommon)文件夹中。

5、改进后的代码放到Remoting事件(服务端广播改进)压缩包中,大家可以比较一下改进后的程序有何不同!

参考资料:
1、 Ingo Rammer,《Advanced .NET Remoting》
2、 吕震宇,《利用Event松耦合远程对象与远程系统
3、 大坏蛋,《.NET Remoting中的事件处理(.NET Framework 2.0)(一)

原文出处:http://www.cnblogs.com/wayfarer/articles/75213.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐