Silverlight应用程序的本地通讯

时间:2021-03-04 20:44:58

Silverlight应用程序的本地通讯

 

       在实际开发中,可能会在同一个网页上放置多个Silverlight应用程序,而这些应用程序之间可能需要互相通讯,比如实现两个Silverlight应用程序之间的同步。

 

         Silverlight 3开始,我们可以使用“System.Windows.Messaging”命名空间中的两个类——LocalMessageSenderLocalMessageReceiver,在运行于同一台计算机上的两个Silverlight应用程序间相互交换信息。

 

         顾名思义,LocalMessageSender类用于发送消息,而LocalMessageReceiver则用于接收消息。

         当一个Silverlight应用程序需要发送消息时,实例化一个LocalMessageSender对象,类似地,如果它需要接收消息,实例化一个LocalMessageReceiver对象。

         消息发送与接收的“配对”关系通过消息接收者的“域名+接收对象名字”来确定。只要“域名+接收对象名字”一致,多个LocalMessageSender对象就可以向同一个LocalMessageReceiver对象发送消息。

         了解了上述知识,开发能相互通讯的Silverlight应用程序就有了章法。

         请看文后所附之示例解决方案LocalCommunication

 

Silverlight应用程序的本地通讯

 

 

图1 在同一个网页上相互通讯的Silverlight程序

        

         1所示,示例解决方案LocalCommunication在同一个ASP.NET网页上承载了两个Silverlight应用程序。用户在发送端应用程序输入一个消息字串,并从下拉框中选择一个命令(“椭圆”或“矩形”)之后,发送端应用程序会将这两个信息以“;”作为间隔符组合成一个复合信息发送出去。

 

         接收端应用程序在收到信息之后,解析收到的信息,将字串显示出来,并依据接收到命令显示一个“椭圆”或“矩形”。

 

         下面剖析一下示例程序,以帮助大家掌握相关的技术要点。

1 消息发送方

在发送端示例程序,需要实例化一个LocalMessageSender对象:

 

    LocalMessageSender Sender = new LocalMessageSender(

                    "LocalCommunication.Receiver");

 

          特别注意一下构造函数中的参数指定了接收者的名字。此名字将用于定义消息的接收者。

 

        实例化LocalMessageSender对象之后,就可以调用其SendAsync()方法“异步”发送信息:

 

    Sender.SendAsync("要发送的信息");

 

         如果需要接收消息接收方发回的“消息已接收”通知,通常会给LocalMessageSender对象的SendCompleted方法挂接一个事件响应函数,在此函数中可以通过其参数e(类型为SendCompletedEventArgs)的Response属性获取用户发回的“回执”。

 

    Sender.SendCompleted += new        

        EventHandler<SendCompletedEventArgs>(Sender_SendCompleted);

 

2 消息接收方

         类似地,消息接收方示例程序实例化一个LocalMessageReceiver对象,注意其名字一定要与消息发送方一致。

 

    LocalMessageReceiver Receiver = new

        LocalMessageReceiver("LocalCommunication.Receiver");

 

         然后,为其挂接事件响应函数,在此函数中处理接收到的消息。

 

    Receiver.MessageReceived += new

        EventHandler<MessageReceivedEventArgs>(Receiver_MessageReceived);

 

         从事件参数e(其类型为MessageReceivedEventArgs)的Message属性可以读取发送方传过来的消息。

 

         最后,调用Listen()方法监听消息:

 

    Receiver.Listen();

 

         最关键的地方就是这么多了。是不是很简单?

 

         但还是有一些东西需要交代的。

 

         1)在消息发送方,LocalMessageSender对象有一个ReceiverDomain属性,默认值为null,这意味着,消息的发送方与接收方必须位于同一个域(Domain)中

 

      注意:

       域(Domain)”和“应用程序域(AppDomain)”不是一回事。域表明了Silverlight应用程序的位置信息,诸如:“www.myuniversity.edu.cn”之类,而应用程序域则代表了Silverlight应用程序的运行环境,Silverlight插件在装载一个Silverlight应用程序时,会为其创建一个应用程序域作为其运行环境。

 

       假设某网页使用多个<object>元素承载了多个Silverlight应用程序,则Silverlight插件会为每个Silverlight应用程序创建相互独立的应用程序域,这些Silverlight应用程序的“域”可以相同(只要来源于同一个网站),也可以不同(如果来源于不同的网站)。

 

         如果将发送方的ReceiverDomain属性设置为LocalMessageSender.Global值(其实就是一个仅包容单个“*”字符的字串),则发送方可以向另一个域的Silverlight应用程序发送信息。

 

         2)在消息接收方,LocalMessageReceiver对象的名字决定了其是否能接收到消息。因此,在同一个域中绝对不要创建拥有相同名字的LocalMessageReceiver对象!

         如果你硬要这么做,当调用LocalMessageReceiver.Listen()方法开始监听时,你会得到一个ListenFailedException,不能接收消息。

 

         为了处理“名字”问题,LocalMessageReceiver类提供了一个NameScope属性,其默认值为“ReceiverNameScope.Domain”,表示这个名字必须在当前域中唯一。它的另一个可选值是“ReceiverNameScope.Global”,表示在当前计算机上运行的所有Silverlight应用程序所涉及到的域中,这个名字都必须是独一无二的。

 

         ReceiverNameScope.Global”值是一个非常强的约束条件,你可以保证在“自己的”域中名字唯一,但你无法保证来源于其他网站的Silverlight应用程序在给LocalMessageReceiver对象取名时一定不会与你“英雄所见略同”。

 

         为了解决这一问题,笔者的建议是给LocalMessageReceiver对象的名字中加上一个GUID值,这样,同名的机会就很少了。

 

         另外,还要注意消息接收方与消息发送方的设置必须一致,比如消息发送方的ReceiverDomain属性设置为“LocalMessageSender.Global”,则接收方的NameScope属性也必须设置为“ReceiverNameScope.Global”。

 

         3)由于消息接收方可能会接收多个消息发送方发来的消息,因此有可能某个“不速之客”(指某个在本机上加载的Silverlight应用程序)“恰巧”地“蒙”对了消息接收对象的名字,这将导致消息接收方应用程序可能会接收到“非法”的,有可能是“恶意”的指令,因此,LocalMessageReceiver类还设计了另一个AllowedSenderDomains属性来限制可以发送消息的域,这个属性是一个“有资格发送消息的”Silverlight应用程序所在域名字串的集合:

 

    public IEnumerable<string> AllowedSenderDomains { get; }

 

         域名不在此集合范围内的Silverlight应用程序发送的消息,将被接收方所拒绝。

 

         可以使用LocalMessageReceiver的另一个构造函数一次性地为上述介绍过的属性进行赋值:

 

    Receiver = new LocalMessageReceiver("LocalCommunication.Receiver",

        ReceiverNameScope.Global,

        new string[] {"www.myFriend.com","www.myHost.com"});

 

         上述代码指定只有来自于“www.myFriend.com”和“www.myHost.com”的Silverlight应用程序可以“跨域”向“我”发送信息。

 

         如果允许接收任何一个域的消息,可以将AllowedSenderDomains属性设置为“LocalMessageReceiver.AnyDomain”值,此值引用仅包容了一个“*”字串的数组对象。

 

         再次警告,这是个“危险”的值,使用时要慎重!

 

         另外,属于同一域内的多个Silverlight应用程序间通讯总是许可的。

         4)如果允许Silverlight应用程序脱离浏览器运行,那么,默认情况下运行于浏览器中的Silverlight应用程序是不能与浏览器外的“兄弟”通讯的。如果需要启用这个功能,必须在创建LocalMessageReceiver对象时,将其DisableSenderTrustCheck属性设置为true(其默认值为false:

 

    Receiver.DisableSenderTrustCheck = true;

 

    下面做个小结:

    可以使用LocalMessageSenderLocalMessageReceiver方便地在运行于同一台计算机上的多个Silverlight应用程序间相互通讯,其中的关键在于要正确地匹配名字和给相关的属性赋与有效的值。

 

         在实际开发中,要特别注意安全性问题。

 

         此外,每次能传送的信息大小是有限制的,最大上限为40K。信息的格式是由程序员决定的,由于Silverlight对序列化XMLJSON格式的数据提供了支持,因此,使用它们会比较方便,只要注意消息大小别越限就好了。

 

==============================

下载本文示例源码

 

注:示例运行环境为VS2010 RC + Silverlight4 RC。