一箭工作室

大家一起来学软件开发

博客园 首页 新随笔 联系 订阅 管理
 使用.Net Remoting的时候,一般情况下,都是客户端调用远程对象方法。但有的时候,需要远程对象调用客户端方法,这时只能通过事件的方式来解决。

远程对象的事件可以分成客户端触发-》服务器应答,服务端触发-》客户端应答和客户端触发-》客户端应答,第一种就很简单了,后面两种都需要有一个中间件。原因很简单,一般情况下,远程对象的程序集是不会放在客户端,不然就会失败远程对象的意义,同时也是为了考虑安全性。如果不能放在客户端,这时远程对象是无法获得客户端程序集。为了解决这个问题,通常会创建一个代理类(也有称为事件重现器),这个事件的代理类在服务器和客户端都会有,最终造成两端的事件消息传递都是通过这个代理类来完成的。

EventProxy类具有相同或者类似的RemoteObject事件,并且分别存在于客户端和服务器端(准确的说是存在于发布给客户端的远程对象程序集中,这个程序集中可以不存在远程对象具体实现类,只有远程对象的接口)。因为远程对象可以调用到EventProxy类,所以当触发事件后,由EventProxy类的方法来实现,不会有任何问题。同时,EventProxy类又可以被客户端调用,所以当EventProxy类捕获到远程对象触发的事件后,再触发被客户端订阅EventProxy类事件。这样,客户端通过间接的方式捕获远程对象捕获的事件。下面看一段代码。

Ø         事件参数

    [Serializable]

    public class CalculateEventArgs

    {

        private string message;

 

        public string Message

        {

            get

            {

                return message;

            }

            set

            {

                message = value;

            }

        }

 

        public CalculateEventArgs()

        {

        }

 

        public CalculateEventArgs(string message)

        {

            this.message = message;

        }

    }

       由于事件参数需要在网络间传输。因此,事件类必须支持可序列化。

Ø         远程对象

public delegate void CalculateStateHandler(object sender,CalculateEventArgs e);

    public class Calculator : MarshalByRefObject

    {

        public event CalculateStateHandler CalculateStateChanged;

        public void LongTimeCalculate()

        {

            OnCalculateStateChanged(new CalculateEventArgs("开始计算!"));

            Thread.Sleep(2000);

            OnCalculateStateChanged(new CalculateEventArgs("计算中!"));

            Thread.Sleep(2000);

            OnCalculateStateChanged(new CalculateEventArgs("计算完成!"));

        }

 

        public void OnCalculateStateChanged(CalculateEventArgs e)//这里有一些特别于本地事件处理的机制。

        {

            //防止客户端因非正常退出,造成服务器触发事件失败。若触发失败,则取消该客户端的事件订阅。

            if (CalculateStateChanged != null)

            {

                foreach(Delegate d in CalculateStateChanged.GetInvocationList())

                {

                    try

                    {

                        ((CalculateStateHandler)d)(this, e);

 

                    }

                    catch

                    {

                        CalculateStateChanged-=(CalculateStateHandler)d;

                    }

                }

            }

        }

}

Ø         事件代理类

    public class CalculateSink : MarshalByRefObject

    {

        public event CalculateStateHandler CalculateStateChanged;

        public CalculateSink()

        {

        }

        public void OnCalculateStateChanged(object sender, CalculateEventArgs e)

        {

            if(CalculateStateChanged!=null)

            {

                CalculateStateChanged(sender,e);

            }

        }

       由于EventProxy也是一个远程对象,只不过这个类很特殊,因为它也要远程对象调用,所以必须和远程对象一样继承于MarshalByRefObject

Ø         客户端代码片断

RemotingConfiguration.Configure("ClientWindowsApplication.exe.config", true);

//获取远程对象

Calculator calculator = new Calculator();

//创建事件代理类,因为本地引用了相应的程序集,所以可以直接创建。

CalculateSink calculateSink = new CalculateSink();

//订阅事件代理类事件,并委托给本地方法处理。

calculateSink.CalculateStateChanged+=new CalculateStateHandler(calculateSink_CalculateStateChanged);

c//订问远程对象事件,并且委托给事件代理类处理。事件代理类捕获到事件后,会触发被本地订阅的事件,让本地方法处理。

alculator.CalculateStateChanged += new CalculateStateHandler(calculateSink.OnCalculateStateChanged);

calculator.LongTimeCalculate();

Ø         服务器端代码

和正常代码没有任何变化。

虽然.NET Remoting事件看上去有点复杂,其实和本地的事件调用和处理基本上都是一样的,关键在于如何设计.NET Remoing的事件,以及对事件异常的处理,这块是在设计和开发过程中难点。比如事件代码中,有一段因客户端程序非正常退出,造成远程对象事件调用列表中,仍然有该客户端的订阅。如果这时触发事件,就会引用服务器端的异常,这块需要特别小心处理。

 

posted on 2007-02-11 20:16  一箭  阅读(443)  评论(0编辑  收藏  举报