代码加咖啡的生活

专门讨论.NET相关技术--大坏蛋
随笔 - 29, 评论 - 253, 引用 - 67

导航

关于


标签

每月存档

最新留言

广告

 

好多人都对Remoting中的事件处理很疑惑,现将完整实现Remoting中事件处理的过程写出来,并对容易犯错误的地方进行总结,希望能给大家一些帮助。
现假设有一个留言板程序:
以下代码中,MsgBoard为以Singleton模式存活于服务器端的共享留言板实例,AddMessage是客户端添加留言的接口,MsgBoard定义如下:

    public class MsgBoard:MarshalByRefObject 
    
{
        
public delegate void EventDelegate(string info);
        
public event EventDelegate OnInfoAdded;
        
public void AddMessage(string info)
        
{
            Console.WriteLine(info);
            if(OnInfoAdded!=null)
                      OnInfoAdded(info);
        }

    }


在有客户端添加留言时,激发一个事件,我们的客户端去订阅该事件来得到留言板更新的通知。
服务器端代码如下:

Using directives

namespace ConsoleServer
{
    
class Program
    
{
        
static void Main(string[] args)
        
{
            RemotingConfiguration.RegisterWellKnownServiceType(
typeof(MyLibrary.MsgBoard), "MyUri", WellKnownObjectMode.

Singleton);
            HttpChannel myChannel 
= new HttpChannel(1080);
            ChannelServices.RegisterChannel(myChannel);

            
//////
            IServerChannelSink sc = myChannel.ChannelSinkChain;
            
while (sc != null)
            
{
                
if (sc is BinaryServerFormatterSink)
                
{
                    ((BinaryServerFormatterSink)sc).TypeFilterLevel 
= TypeFilterLevel.Full;
                    
//break;
                }

                
if (sc is SoapServerFormatterSink)
                
{
                    ((SoapServerFormatterSink)sc).TypeFilterLevel 
= TypeFilterLevel.Full;
                    
//break;
                }

                sc 
= sc.NextChannelSink;
            }

            Console.WriteLine("Server Started");
            Console.ReadLine();
        }

    }

}


我们可以不要详细去关心服务器端程序的代码,我们只需要知道:
1,服务器注册了远程服务的类型
2,TypeFilterLevel = TypeFilterLevel.Full
   由于从.NET Framework 1.1起,缺省情况下DelegateSerializationHolder不允许被反序列化,(即FormatterSink.TypeFilterLevel = TypeFilterLevel.low)。为了实现远程事件处理,我们必须解除该约束,使ServerFormatterSink.TypeFilterLevel = TypeFilterLevel.Full

我们更加需要关心的是我们的客户端代码:

Using directives


namespace ConsoleClient
{
    
class Program
    
{
        
static void Main(string[] args)
        
{
            
try
            
{
                RemotingConfiguration.RegisterWellKnownClientType(
typeof(MyLibrary.MsgBoard), "Http://localhost:1080/MyUri

");
                HttpChannel myChannel = new HttpChannel(1000);
                ChannelServices.RegisterChannel(myChannel);
                IServerChannelSink sc 
= myChannel.ChannelSinkChain;
                Console.WriteLine(
"Client Started");
                MyLibrary.MsgBoard msgbd 
= new MyLibrary.MsgBoard();
                
///msgbd.OnInfoAdded += new MyLibrary.MsgBoard.EventDelegate(msgbd_OnInfoAdded);
                MyLibrary.eventClass evclass = new MyLibrary.eventClass();
                msgbd.OnInfoAdded 
+= new MyLibrary.MsgBoard.EventDelegate(evclass.msgbd_OnInfoAdded);///instead
                msgbd.AddMessage("Hello all");
                Console.ReadLine();
            }

            
catch (Exception exc)
            
{
                Console.WriteLine(exc.StackTrace);
                Console.ReadLine();
            }

        }


        
public static void msgbd_OnInfoAdded(string info)
        
{
            Console.WriteLine(
"info on server event:{0}", info);
        }

    }


}



请注意:此处我们使用一个实例方法去订阅服务器组件的事件,该实例类型定义如下:

    public class eventClass:MarshalByRefObject 
    
{
        
public void msgbd_OnInfoAdded(string info)
        
{
            Console.WriteLine(
"info from server event:{0}", info);
        }

    }


为什么要这么做?
.NET Framework要求,事件的发布者必须拥有事件订阅者的元数据,而提供元数据的简单方法,就是让服务器程序添加对客户端程序的引用,但事实上我们不需要这么做,我们将订阅者声明在远程类的程序集中,而该程序集的元数据原本就是服务器和客户端共有的。此时我们要注意到,订阅事件的类,也被申明成MarshalByRefObject,这是.NET Framework 2.0出现的一个限制,委派在序列化信息中包含了函数对应实例的地址,在服务器端回掉时,可以寻址到客户端订阅的对象实例并执行相应的成员方法,既然能被服务器寻址,则该订阅对象要求是MarshalByRefObject,这不难理解。在.NET Framework 2.0之前,我们可以使用一个包含静态函数的委派去订阅服务器组件的事件,但2.0以后,如果用一个静态成员去订阅,该响应会在服务器空间内被执行,所以我们要记住,远程处理总是处理某种形式的实例成员,而静态成员不能。
总结一下:
要实现Remoting事件远程处理,要记住以下几个原则:
1,缺省情况下DelegateSerializationHolder不允许被反序列化
2,事件发起者必须拥有订阅者的元数据
3,远程处理总是处理实例成员,且该实例必须是MarshalByReference

附加:

由于HttpChannel将ChannelSinkChain作为公开的属性所以我们能够直接修改TypeFilterLevel,而队TcpChannel,ChannelSinkChain是无法直接访问的,我们可以在监视窗口看到SinkProvider,我们只能在构造TcpChannel时去干涉它,HttpChannel和TcpChannel都可以使用配置文件的方法,但代码有助于理解。
以下是HttpChannel修改TypeFilterLevel的示例:
BinaryServerFormatterSinkProvider serverProv = new BinaryServerFormatterSinkProvider();
serverProv.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
BinaryClientFormatterSinkProvider clientProv = new BinaryClientFormatterSinkProvider();
IDictionary props = new Hashtable();
props["port"] = 1080;

// HttpChannel chan = new HttpChannel(props, clientProv, provider);
// ChannelServices.RegisterChannel(chan);

RemotingConfiguration.RegisterWellKnownServiceType(typeof(MyLibrary.MsgBoard), "MyUri", WellKnownObjectMode.Singleton);
TcpChannel myChannel = new TcpChannel(props, clientProv, serverProv);
ChannelServices.RegisterChannel(myChannel);

 


 

打印 | 张贴于 2004-11-09 00:05:00 | Tag:.NET Remoting

留言反馈

#re: .NET Remoting中的事件处理 编辑
犯错误了^_^
客户端是否必须设置一个端口号才能接收消息?如果只是服务器简单的广播怎么实现?
2005-10-24 10:37:00 | [匿名:初学者]
#re: .NET Remoting中的事件处理 编辑
如题
2005-10-24 10:34:00 | [匿名:客户端是否必须设置一个端口号才能接收消息?如果只是服务器简单的广]
#re: .NET Remoting中的事件处理 编辑
有一个很重要的问题,中间的这个实现类“MsgBoard:MarshalByRefObject ” ,是否必须也在客户端保留其dll文件,如果这样,岂不是太麻烦了??
如果内部实现有改动,怎么通知客户端也重新加载?因为客户端在哪里都不知道阿。
2005-07-20 11:40:00 | [匿名:刘士旺]
#re: .NET Remoting中的事件处理 编辑
客户端在ISA防火墙后面还可以用吗?
2005-06-13 20:33:00 | [匿名:ichs]
#re: .NET Remoting中的事件处理 编辑
好好好
2005-05-11 22:12:00 | [匿名:1]
#re: .NET Remoting中的事件处理 编辑
你好 这个很好 我看了
2005-05-11 22:08:00 | [匿名:张]
#re: .NET Remoting中的事件处理 编辑
hao
2005-05-11 22:08:00 | [匿名:zhang ]
#re: .NET Remoting中的事件处理 编辑
好贴,正好要用.NET,收藏
2005-04-30 14:57:00 | [匿名:oofrank]
#re: .NET Remoting中的事件处理 编辑
it should be worth of researching

^^^^^^^^^^^^^^^^^^^
汗,又错了,be worth researching
2005-04-19 19:02:00 | [匿名:rainbow]
#re: 编辑
Good!
2005-04-08 16:02:00 | [匿名:尖锐湿疣]
#re: .NET Remoting中的事件处理 编辑
我在 .NET 2003中用你的方法,怎么不行?
2005-03-19 15:32:00 | [匿名:MULINTAO]
#re: .NET Remoting中的事件处理 编辑
虚心请教“大坏蛋”,如果将委托改成
public delegate void EventDelegate(object sender,SomeEventArgs e) 却出现了不能引用objref的问题?怎么样解决那?为什么那?
2005-02-28 19:46:00 | [匿名:masathem]
#re: .NET Remoting中的事件处理 编辑
没有问题,在有必要的情况下可以使用多个通道,如客户端可能分布在内网,外网,甚至本机,可以通过多个通道来权衡不同的需要,但要知道,注册多个通道会增加通讯量,以及增加服务器负载
2005-01-19 14:18:00 | [匿名:大坏蛋]
#re: .NET Remoting中的事件处理 编辑
一个远程对象,能不能同时启两个通道?tcp/http

一个用于和web页通信,一个高知winform?
2005-01-19 11:42:00 | [匿名:阿三]
#re: .NET Remoting中的事件处理 编辑
太好啦,太好啦,真是茅塞顿开!感谢大坏蛋!您是世界上最好的人!
2004-11-18 13:27:00 | [匿名:Hanbf]
#re: .NET Remoting中的事件处理 编辑
hoho,正好用到,感谢这位仁兄
2004-11-12 20:29:00 | [匿名:robaggio]
#re: .NET Remoting中的事件处理 编辑
好。 本人近两年也一直在做remoting开发, 但是进来遇到了一个比较棘手的问题, 希望能得到高手相助。 我的QQ是52076319, 请加我。 若能指点一二 , 不胜感激啊。。。。。
2004-11-12 15:49:00 | [匿名:大理段氏]
#re: .NET Remoting中的事件处理 编辑
这就是传说中的LCE(Loosely Coupled Events)? 能否在给一些关于这方面的详细资料。谢谢。
2004-11-10 13:24:00 | [匿名:SmartGuy]
#re: .NET Remoting中的事件处理 编辑
...燕雀丐知鸿鹄之志.
^


中文也有语法错误 ^O^
2004-11-09 17:05:00 | [匿名:Chainet]
#re: .NET Remoting中的事件处理 编辑
语法错误,^_^。。。牛,有品味!
2004-11-09 15:55:00 | [匿名:cgyu]
#re: .NET Remoting中的事件处理 编辑
靠,就是因为english的writing skill差才练习的,如果很吊的话就不会拿出来下现,燕雀丐知鸿鹄之志.
(it should be worth of researching)
2004-11-09 15:40:00 | [匿名:leighsword]
#re: .NET Remoting中的事件处理 编辑
我并不觉得将一句有语法错误的“英文”句子放在话后有助于提高你的品味。
2004-11-09 15:13:00 | [匿名:辅子彻]
#re: .NET Remoting中的事件处理 编辑
值得研究一下.
(it worth for a research)
2004-11-09 14:53:00 | [匿名:leighsword]
#re: .NET Remoting中的事件处理 编辑
sure!No problem
2004-11-09 10:27:00 | [匿名:大坏蛋]
#re: .NET Remoting中的事件处理 编辑
Can the HttpChannel across the firewall?
2004-11-09 10:23:00 | [匿名:leighsword]
对不起,目前本随笔不允许发表新评论.

Powered by: Joycode.MVC引擎 0.5.2.0