然而,我们要明白一个事实:就是服务端和客户端是处于两个不同的应用程序域中。因此在Remoting中,客户端获得的远程对象实际是服务端注册对象的代理。如果我们在注册后,人工去创建一个实例,而非Remoting在激活后自动创建的对象,那么客户端获得的对象与服务端人工创建的实例是两个迥然不同的对象。客户端获得的代理对象并没有指向你刚才创建的obj实例。所以obj发送的消息,客户端根本无法捕捉。
那么,我们只有望洋兴叹,束手无策了吗?别着急,别忘了在服务器注册对象方法中,还有一种方法,即Marshal方法啊。还记得Marshal的实现方式吗?
BroadCastObj Obj = new BroadCastObj(); ObjRef objRef = RemotingServices.Marshal(Obj,"BroadCastMessage.soap");
这个方法与前不一样。前面的三种方式,远程对象是根据客户端调用的方式,来自动创建的。而Marshal方法呢?则显式地创建了远程对象实例,然后将其Marshal到通道中,形成ObjRef指向对象的代理。只要生命周期没有结束,这个对象就一直存在。而此时客户端获得的对象,正是创建的Obj实例的代理。
OK,这个问题解决了,我们来看看具体实现。
公共程序集和远程对象与前相似,就不再赘述,只附上代码:
公共程序集:
public delegate void BroadCastEventHandler(string info); public interface IBroadCast { event BroadCastEventHandler BroadCastEvent; void BroadCastingInfo(string info); }
远程对象类:
public event BroadCastEventHandler BroadCastEvent; #region IBroadCast 成员 //[OneWay] public void BroadCastingInfo(string info) { if (BroadCastEvent != null) { BroadCastEvent(info); } } #endregion public override object InitializeLifetimeService() { return null; }
下面,该实现服务端了。在实现之前,我还想罗嗦几句。在第一节中,我们实现了服务端订阅客户端事件。由于订阅事件是在服务端发生的,因此事件本身并未被传送。被序列化的仅仅是传递的消息,即Fax而已。现在,方向发生了改变,传送消息的是服务端,客户端订阅了事件。但这个事件是放在远程对象中的,因此事件必须被序列化。而在.Net Framework1.1中,微软对序列化的安全级别进行了限制。有关委托和事件的序列化、反序列化默认是禁止的,所以我们应该将TypeFilterLevel的属性值设置为Full枚举值。因此在服务端注册通道的方式就发生了改变:
private void StartServer() { BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider(); BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider(); serverProvider.TypeFilterLevel = TypeFilterLevel.Full; IDictionary props = new Hashtable(); props["port"] = 8080; HttpChannel channel = new HttpChannel(props,clientProvider,serverProvider); ChannelServices.RegisterChannel(channel); Obj = new BroadCastObj(); ObjRef objRef = RemotingServices.Marshal(Obj,"BroadCastMessage.soap"); }
注意语句serverProvider.TypeFilterLevel = TypeFilterLevel.Full;此语句即设置序列化安全级别的。要使用TypeFilterLevel属性,必须申明命名空间:
using System.Runtime.Serialization.Formatters;
而后面两条语句就是注册远程对象。由于在我的广播程序中,发送广播消息是放在另一个窗口中,因此我将该远程对象声明为公共静态对象:
public static BroadCastObj Obj = null;
然后在调用窗口事件中加入:
private void ServerForm_Load(object sender, System.EventArgs e) { StartServer(); lbMonitor.Items.Add("Server started!"); }
来看看界面,首先启动服务端主窗口:
我放了一个ListBox控件来显示一些信息,例如显示服务器启动了。而BroadCast按钮就是广播消息的,单击该按钮,会弹出一个对话框:
BraodCast按钮的代码: