科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道[冷枫]Microsoft .Net Remoting系列专题之三:Remoting事件处理全接触

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

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

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

作者:冷枫 来源:CSDN 2007年9月23日

关键字:

  • 评论
  • 分享微博
  • 分享邮件

在本页阅读全文(共3页)

四、 一点补充

我在前面的事件处理中,使用的都是默认的EventArgs。如果要定义自己的EventArgs,就不相同了。因为该信息是传值序列化,因此必须加上[Serializable],且必须放到公共程序集中,部署到服务端和客户端。例如:
[Serializable]
public class BroadcastEventArgs:EventArgs
{
 private string msg = null;
 public BroadcastEventArgs(string message)
 {
  msg = message;
 }

 public string Message
 {
  get {return msg;}
 }
}

五、持续改进(经Beta的提醒,我改进了我的程序,并对文章进行了修改 2004年12月13日)

也许,细心的读者注意到了,在我的远程对象类和EventWrapper类中,触发事件方法的Attribute[OneWay]被我注释掉了。我看到很多资料上写到,在Remoting中处理事件,触发事件的方法必须具有这个Attribute。这个attribute究竟有什么用?

在发送事件消息的时候,事件的订阅者会触发事件,然后响应该事件。然而当事件的订阅者发生错误的时候呢?例如,发送事件消息的时候,才发现根本没有事件订阅者;或者事件的订阅者出现故障,如断电、或异常关机。此时,发送事件一方会因为找不到正确的事件订阅者,而发生异常。以我的程序为例。当我们分别打开服务端和客户端程序的时候,此时广播信息正常。然而,当我们关闭客户端后,由于该客户端没有取消订阅,此时异常发生,提示信息如图:

(不知道为什么,这个异常与客户端连接服务端出现的异常一样。这个异常容易让人产生误会。)

如果这个时候我们同时打开了多个客户端,那么其他客户端就会因为这一个客户端关闭造成的错误,而无法收到广播信息。那么让我们先做第一步改进:

1、先考虑正常情况。在我的客户端,虽然提供了取消订阅的操作,但并没有考虑用户关闭客户端的情况。即,关闭客户端时,并未取消事件的订阅,所以我们应该在关闭客户端窗体中写入:

        private void ClientForm_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        
{
            watch.BroadCastEvent 
-= new BroadCastEventHandler(wrapper.BroadCasting);
        }

2、仅仅是这样还不够。如果客户端并没有正常关闭,而是因为突然断电而导致客户端关闭呢?此时,客户端还没有来得及取消事件订阅呢。在这种情况下,我们需要用到OneWayAttribute。

前面说到,发送事件一方如果找不到正确的事件订阅者,会发生异常。也就是说,这个事件是unreachable的。幸运的是,OneWayAttribute恰好解决了这个问题。其实从该特性的命名OneWay,大约也能猜到其中的含义。当事件不可到达,无法发送时,正常情况下,会返回一个异常信息。如果加上OneWayAttribute,这个事件的发送就变成单向的了。假如此时发生异常,那么系统会自动抛掉该异常信息。由于没有异常信息的返回,发送信息方会认为发送信息成功了。程序会正常运行,错误的客户端被忽略,而正确的客户端仍然能够收到广播信息。

因此,远程对象的代码就应该是这样:

public event BroadCastEventHandler BroadCastEvent;

IBroadCast 成员

public override object InitializeLifetimeService()
{
 
return null;
}

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事件(服务端广播改进)压缩包中,大家可以比较一下改进后的程序有何不同!

 

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=727789

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章