.NET Remoting的广播和回调问题

.NET Remoting的广播和回调问题

似乎很多人都用.NET Remoting来做有广播和回调(包括事件)的Client-Server程序,虽然对这种不正常用法不太喜欢,但最近也切身趟了一次这滩浑水。注:阅读本文需要对Remoting有一定的基本了解。

说这种做法不正常,是因为.NET Remoting在很大程度上是针对Web Service设计的。也就是说,它的通信模式是Web的Client端主动请求模式,Server端对每次请求的处理都是独立的,也不会主动去访问Client端。这就是说,Server向Client主动发信息,不管是广播还是事件通知,都是非标准用法,并且缺省安全配置下是禁止的。也就是说,.NET Remoting不是用来做这个的。

那为什么还要用它做这个活呢?因为.NET Remoting的用途不只是网络上的Client-Server通信,还用于跨越AppDomain的本机通信,尤其是进程间通信。另外在局域网内,使用广播和回调也不担心安全问题。而且使用.NET Remoting的IpcChannel是唯一可以使用NT的命名管道通信的现成API,否则就得调用Win32 API。

因为缺省的安全配置不允许,所以要将安全级别改成最低,需要在创建Channel时指定Server Sink。这里以Binary Formatter和IpcChannel为例:
BinaryClientFormatterSinkProvider clientSink = new BinaryClientFormatterSinkProvider();
BinaryServerFormatterSinkProvider serverSink = new BinaryServerFormatterSinkProvider();
serverSink.TypeFilterLevel = TypeFilterLevel.Full;
IDictionary properties = new Hashtable();
properties["portName"] = "localhost:9090";
IpcChannel channel = new IpcChannel(properties, clientSink, serverSink);
ChannelServices.RegisterChannel(channel, false);

RemotingConfiguration.RegisterWellKnownServiceType(
                typeof(MyServer), "MyServer", WellKnownObjectMode.SingleCall);

要注意的是Server主动通知Client的本质是Client自己也创建了个Server,然后Server和Client的位置颠倒,由Server主动去向Client发请求。所以,如果在同一台主机上,Client和Server的端口号不能一样。对命名管道而言,其实是命名管道的名字不能一样。Client端样例:
IpcChannel channel = new IpcChannel("localhost:9091");
ChannelServices.RegisterChannel(channel, false);
MyServer server = (MyServer)Activator.GetObject(typeof(MyServer), "ipc://localhost:9090/MyServer");

那么然后是如何定义MyServer的接口了。在MyServer上定义event不是一个好办法,因为.NET Remoting机制要求Server必须知道响应事件的对象的类型,即Server必须引用Client的Assembly。这使得Client完全无法独立扩展,而且Server和Client必须一起编译以保证交互接口完全一致。一个较好的办法是使用一个interface,Client端定义一个实现该interface的类,并将对象的引用传给Server,Server就可以通过该interface调用Client端的函数了。这样,Client和Server都只需要引用定义该interface的Assembly,而不需要知道具体的实现类型。当然,实现该interface的对象必须是可用于Remoting的,即MarshalByRef或Serializable。Server端如下:
public class MyServer : MarshalByRefObject
{
  public void AddCallback(ICallback callback) { ... }
}

public interface ICallback
{
  void Notify();
}

Client端如下:
class MyClient : ICallback
{
  void ICallback.Notify() { ... }
}

另外注意不要在MyServer里用成员变量保存数据和对象引用,因为Server的接口对象的作用只是接口,随时可能被丢弃或重新创建,即使用Singleton模式也是一样,即使使用Object Life Time Lease Management显示指定也不能保证,谁也不能保证Server不当机、网络不断线。除非是可以不在乎出错的情况,否则严格遵循每次对Server的请求都是独立的这一条约定。必须要保存的数据可以交给Singleton的对象或数据库。

更新:以上说的是WellKnownServiceType的情况,如果使用Client激活模式则是另一种用法。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值