在c/s应用中,有时需要从服务端回调客户端的程序,在.Net下,通过事件委托机制可以很简单的进行实现,
因为服务端需要调用客户端程序,它必须知道客户端程序的存根(即类定义),
但我们应该对程序逻辑实现进行隔离,即服务端不应该包括客户端具体的实现代码,
在这个前提下,我们引入了一个ServiceAdapter类,定义如下:
- public class ServiceAdapter : MarshalByRefObject
- {
- public event Callback OnCallback;
- public void Callback(object obj)
- {
- if (OnCallback != null)
- OnCallback(obj);
- }
- }
ServiceAdapter是一个远程对象,这样服务端才能调用它。Callback是一个委派对象,定义如下:
- public delegate void Callback(object obj);
回调(callback)的思路
在客户端:
创建ServiceAdapter对象,并对OnCallback委派进行赋值,然后把Callback方法传递给服务端;
在服务端:
将客户端传递过来的Callback委派方法进行登记,在需要回调客户端时,遍历Callback委派进行调用,
显然,回调是由ServiceAdapter完成的,这也是ServiceAdapter必须是远程对象的原因。
部分实现代码
客户端:
- IChannel chnl = new TcpChannel(0);
- ChannelServices.RegisterChannel(chnl, false);
- ServiceAdapter adapter = new ServiceAdapter();
- adapter.OnCallback += new Callback(client_OnCallback);
- ISomeService ss = (ISomeService)Activator.GetObject(typeof(ISomeService), "tcp://localhost:8080/someservice.rem");
- ss.RegisterClient(new Random().Next(1000, 9999).ToString(), adapter.Callback);
第1,2行很重要,用于注册一个信道,用于服务端回调。
服务端:
服务注册
- BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
- serverProvider.TypeFilterLevel = TypeFilterLevel.Full;
- IDictionary dict = new Hashtable();
- dict["port"] = 8080;
- channel = new TcpChannel(dict, null, serverProvider );
- ChannelServices.RegisterChannel(channel, false);
- RemotingConfiguration.ApplicationName = "SomeService";
- WellKnownServiceTypeEntry service = new WellKnownServiceTypeEntry(typeof(SomeServiceImpl), "SomeService.rem", WellKnownObjectMode.Singleton);
- RemotingConfiguration.RegisterWellKnownServiceType(service);
第1,2行为设置安全级别,默认为Low。
回调客户端
- IDictionary<string, Callback> temp = new Dictionary<string, Callback>(callbacks);
- foreach (string client in temp.Keys)
- { // 遍历所有callback.
- try
- {
- temp[client](obj);
- }
- catch
- { // 如出现异常,则移除callback登记.
- callbacks.Remove(client);
- }
- }