Silverlight应用程序的本地通讯
在实际开发中,可能会在同一个网页上放置多个Silverlight应用程序,而这些应用程序之间可能需要互相通讯,比如实现两个Silverlight应用程序之间的同步。
从Silverlight 3开始,我们可以使用“System.Windows.Messaging”命名空间中的两个类——LocalMessageSender和LocalMessageReceiver,在运行于同一台计算机上的两个Silverlight应用程序间相互交换信息。
顾名思义,LocalMessageSender类用于发送消息,而LocalMessageReceiver则用于接收消息。
当一个Silverlight应用程序需要发送消息时,实例化一个LocalMessageSender对象,类似地,如果它需要接收消息,实例化一个LocalMessageReceiver对象。
消息发送与接收的“配对”关系通过消息接收者的“域名+接收对象名字”来确定。只要“域名+接收对象名字”一致,多个LocalMessageSender对象就可以向同一个LocalMessageReceiver对象发送消息。
了解了上述知识,开发能相互通讯的Silverlight应用程序就有了章法。
请看文后所附之示例解决方案LocalCommunication。
图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;
下面做个小结:
可以使用LocalMessageSender和LocalMessageReceiver方便地在运行于同一台计算机上的多个Silverlight应用程序间相互通讯,其中的关键在于要正确地匹配名字和给相关的属性赋与有效的值。
在实际开发中,要特别注意安全性问题。
此外,每次能传送的信息大小是有限制的,最大上限为40K。信息的格式是由程序员决定的,由于Silverlight对序列化XML和JSON格式的数据提供了支持,因此,使用它们会比较方便,只要注意消息大小别越限就好了。
==============================
注:示例运行环境为VS2010 RC + Silverlight4 RC。