文本版|topic 高级搜索
   名人堂 帮助 论坛制度 意见反馈 | 首页 博客 周新贴 专题 求职 读书
RSS 底部
 
社区导航: 专家门诊   网络技术   操作系统   数据库   程序设计   系统应用   考试认证   CIO及信息化   站长交流   综合交流   下载基地  51CTO产品服务 设为首页 | 收藏本站
51CTO技术论坛» .Net » 用 .Net Remoting 技术实现“定向广播”       [ 打印]  [ 订阅]  [ 收藏]  [ 推荐给朋友]   [ 本帖文本页]

论坛跳转:
     
标题: [转载] 用 .Net Remoting 技术实现“定向广播”  ( 查看:205  回复:0 )   
 
Easy
助理工程师  点击可查看详细



帖子 365
精华 2
无忧币 1780
积分 1619
阅读权限 40
注册日期 2007-11-6
最后登录 2008-5-5 离线

[查看资料]  [发短消息]  [Blog
       
发表于:2007-11-16 14:17   标题:用 .Net Remoting 技术实现“定向广播”
上一帖 |
相对于 WebService 来说,采用 .Net Remoting 技术的客户端能够订阅服务器端事件,这个功能简直太棒了。

如果想利用该技术作一个简单而又典型的应用,信息广播程序是一个不错的选择。以下代码是一个简单的广播程序,当然,它实在太简陋了。

服务端:

Code
Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->class Program
{
    static void Main(string[] args)
    {
        BinaryServerFormatterSinkProvider sfsp = new BinaryServerFormatterSinkProvider();
        sfsp.TypeFilterLevel = TypeFilterLevel.Full;
        Hashtable props = new Hashtable();
        props["port"] = 8086;
        TcpChannel channel = new TcpChannel(props, null, sfsp);
        ChannelServices.RegisterChannel(channel, false);

        SayHello sayHello = new SayHello();
        RemotingServices.Marshal(sayHello, "SayHello");

        Console.ReadKey();

        sayHello.Say("Mike", "Hello, Mike");

        Console.ReadKey();

        sayHello.Say("John", "Hello, John");

        Console.ReadKey();
    }
}
客户端:
Code
Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->class Program
{
    static void Main(string[] args)
    {
        BinaryServerFormatterSinkProvider sfsp = new BinaryServerFormatterSinkProvider();
        sfsp.TypeFilterLevel = TypeFilterLevel.Full;
        Hashtable props = new Hashtable();
        props["port"] = 0;
        TcpChannel channel = new TcpChannel(props, null, sfsp);
        ChannelServices.RegisterChannel(channel, false);

        SayHello sh = (SayHello)Activator.GetObject(typeof(SayHello), "tcp://localhost:8086/SayHello");
        SayEventReappear re = new SayEventReappear();
        re.ClientId = "John";
        sh.OnSay += new SayHandler(re.Say);
        re.OnSay += new SayHandler(re_OnSay);            

        Console.ReadKey();
    }

    static void re_OnSay(string text)
    {
        Console.WriteLine(text);            
    }
}

远程对象、委托及事件重现器(需同时部署在服务端及客户端):
Code
Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public class SayHello : MarshalByRefObject
{
    public event SayHandler OnSay;

    public void Say(string clientId, string text)
    {
        if (this.OnSay != null) this.OnSay(text);
    }
}

public delegate void SayHandler(string text);

public class SayEventReappear : MarshalByRefObject
{
    public event SayHandler OnSay;

    public void Say(string text)
    {
        if (this.OnSay != null) this.OnSay(text);
    }
}
OK,我的信息广播程序就这样完成了。

但是,我很快就发现了问题:如果我的确想让所有订阅我的广播事件的客户端都得到我要广播的信息,这个实现应该不会有问题。但是现在我有一个消息只想通知 Mike 或 John (正如以上代码),(注:可能这时不能再称为“广播”了),我的广播程序依然将这个消息通知到了每一个客户端。

可以想到的一个方法是,让事件重现器(SayEventReappear)接收到信息后先判断一下是不是发给自己的,只有发给自己的信息才激发本地事件(代码比较容易实现,不再贴出源码)。但是,这种处理只是在客户端将信息忽略而己,服务器端是照常广播了,如果你的信息非常机密,或者带宽非常有限,这显然不是好的解决办法。

考虑到 .Net Remoting 客户端订阅事件的实现原理:事件重现器在客户端实例化,并由服务器对按引用方式对其远程调用(个人理解,未经确认,欢迎指正)。如果将事件重现器订阅服务器端事件改为向服务器端“注册”事件重现器,逻辑上应该可行,这样就可以让每个客户端注册的事件重现器携带自己的客户标识,让服务端根据标识决定是否引发特定客户端的事件。修改后的代码如下:
远程对象:
Code
Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public class SayHello : MarshalByRefObject
{
    private ListSayEventReappear> reList = new ListSayEventReappear>();

    public void Say(string clientId, string text)
    {
        foreach (SayEventReappear re in this.reList)
        {
            if (re.ClientId == clientId) re.Say(text);
        }
    }

    public void AddEventReappear(SayEventReappear re)
    {
        this.reList.Add(re);
    }
}
客户端程序:
sh.OnSay += new SayHandler(re.Say);改为
sh.AddEventReappear(re);
OK,服务端分别检测每一个客户端的Id,然后只引发特定客户端的事件,真正的“定向广播”实现了。

但是检查一下以上代码,会发现依然存在问题:由于事件重现器的实例存在于客户端,服务端访问的是它的代理类,因此每次对 ClientId 的检查都是一次远程调用,这无疑是一种浪费。因此将程序改为在客户端注册事件重现器时提交 ClientId 或许更合理。修改后的代码如下:
远程对象:
Code
Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public class SayHello : MarshalByRefObject
{
    private Dictionarystring, SayEventReappear> reDict = new Dictionarystring, SayEventReappear>();

    public void Say(string clientId, string text)
    {
        foreach (KeyValuePairstring, SayEventReappear> kp in this.reDict)