惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

美团技术团队
J
Java Code Geeks
有赞技术团队
有赞技术团队
GbyAI
GbyAI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 叶小钗
阮一峰的网络日志
阮一峰的网络日志
Microsoft Security Blog
Microsoft Security Blog
IT之家
IT之家
G
Google Developers Blog
月光博客
月光博客
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
S
SegmentFault 最新的问题
博客园 - 三生石上(FineUI控件)
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - Franky
腾讯CDC
V
Visual Studio Blog
博客园 - 【当耐特】
D
Docker
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Engineering at Meta
Engineering at Meta
L
LangChain Blog

博客园 - 编程山人

品牌大全 控制只生成一个子窗体(简单) C#类中的get 和set 函数的具体用法 What's TM? 在C#中利用SharpZipLib进行文件的压缩和解压缩 在C#中运用SQLDMO备份和恢复Microsoft SQL Server数据库 WinForm实现SQLServer存储图片 通过C#调用CHM帮助文件 [Remoting专题系列] 一:.NET Remoting [Remoting专题系列] 二:远程对象 [Remoting专题系列] 三:激活模式 [Remoting专题系列] 四:生存期租约 [Remoting专题系列] 五:信道 [Remoting专题系列] 六:异步调用 [Remoting专题系列] 七:调用上下文 [Remoting专题系列] 八:元数据 [Remoting专题系列] 九:动态发布 [Remoting专题系列] 十:追踪服务 [Remoting专题系列] 十二:配置文件
[Remoting专题系列] 十一:事件
编程山人 · 2007-06-05 · via 博客园 - 编程山人

在 Remoting 中使用 Event 主要是为了实现 CallBack 机制,让服务器在接收到某个 "消息" 时,主动调用某个或多个客户端的方法。

我们先看一个例子。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

namespace Learn.Library.Remoting
{
  
/// <summary>
  
/// 委托类型
  
/// </summary>

  public delegate void TestHandler();

  
/// <summary>
  
/// 远程类型
  
/// </summary>

  public class Data : MarshalByRefObject
  
{
    
public TestHandler OnTest;

    
public void Test()
    
{
      Console.WriteLine(
"Test(AppDomain:{0})", AppDomain.CurrentDomain.FriendlyName);
      
if (OnTest != null) OnTest();
    }

  }


  
public class RemotingTest2
  
{
    
/// <summary>
    
/// 服务器端代码
    
/// </summary>

    static void Server()
    
{
      AppDomain server 
= AppDomain.CreateDomain("server");
      server.DoCallBack(
delegate
      
{
        BinaryServerFormatterSinkProvider bin 
= new BinaryServerFormatterSinkProvider();
        bin.TypeFilterLevel 
= TypeFilterLevel.Full;

        TcpServerChannel channel 
= new TcpServerChannel("server"801, bin);
        ChannelServices.RegisterChannel(channel, 
false);

        RemotingConfiguration.RegisterWellKnownServiceType(
typeof(Data), "data"
          WellKnownObjectMode.Singleton);
      }
);
    }


    
/// <summary>
    
/// 客户端代码
    
/// </summary>

    static void Client()
    
{
      TcpClientChannel channel 
= new TcpClientChannel();
      ChannelServices.RegisterChannel(channel, 
false);
      RemotingConfiguration.RegisterWellKnownClientType(
typeof(Data), "tcp://localhost:801/data");

      Data data 
= new Data();
      data.OnTest 
+= delegate 
      

        Console.WriteLine(
"OnTest(AppDomain:{0})", AppDomain.CurrentDomain.FriendlyName); 
      }
;
      data.Test();
    }


    
static void Main()
    
{
      Server();
      Client();
    }

  }

}


输出:
Test(AppDomain:server)
OnTest(AppDomain:server)

运行结果表明客户端事件方法 OnTest 被顺利执行。只不过结果有点问题,OnTest 是在服务器程序域内执行,这显然和我们设想服务器去通知客户端有所出入。这种方式实质上是将客户端委托方法一起序列化为消息传递到服务器端,然后在服务器应用程序域被执行,因此客户端是无法接收到所谓 
"回调消息" 的。

要实现我们所需要的 Remoting Event,需要做如下步骤:

1. 采取所谓 Duplex 方式。也就是说在客户端和服务器同时启用 ServerChannel 和 ClientChannel,因此我们需要使用 HttpChannel 或 TcpChannel。
2. 客户端事件方法应该是一个继承自 MarshalByRefObject 类型的实例方法。因为服务器是通过创建客户端的 MBR SAO 对象来实现回调的。
3. 缺省情况下,Delegate 无法被序列化,因此我们需要将服务器的 Formatter.TypeFilterLevel 设置为 Full。

修改后的代码。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

namespace Learn.Library.Remoting
{
  
/// <summary>
  
/// 委托类型
  
/// </summary>

  public delegate void TestHandler();

  
/// <summary>
  
/// 远程类型
  
/// </summary>

  public class Data : MarshalByRefObject
  
{
    
public TestHandler OnTest;

    
public void Test()
    
{
      Console.WriteLine(
"Test(AppDomain:{0})", AppDomain.CurrentDomain.FriendlyName);
      
if (OnTest != null) OnTest();
    }

  }


  
/// <summary>
  
/// 客户端远程类型
  
/// </summary>

  public class ClientData : MarshalByRefObject
  
{
    
public void OnTestMethod()
    
{
      Console.WriteLine(
"Test(AppDomain:{0})", AppDomain.CurrentDomain.FriendlyName);
    }

  }


  
public class RemotingTest2
  
{
    
/// <summary>
    
/// 服务器端代码
    
/// </summary>

    static void Server()
    
{
      AppDomain server 
= AppDomain.CreateDomain("server");
      server.DoCallBack(
delegate
      
{
        BinaryClientFormatterSinkProvider cbin 
= new BinaryClientFormatterSinkProvider();
        BinaryServerFormatterSinkProvider sbin 
= new BinaryServerFormatterSinkProvider();
        sbin.TypeFilterLevel 
= TypeFilterLevel.Full;

        Hashtable properties 
= new Hashtable();
        properties[
"port"= 801;

        TcpChannel channel 
= new TcpChannel(properties, cbin, sbin);
        ChannelServices.RegisterChannel(channel, 
false);

        RemotingConfiguration.RegisterWellKnownServiceType(
typeof(Data), "data"
          WellKnownObjectMode.Singleton);
      }
);
    }


    
/// <summary>
    
/// 客户端代码
    
/// </summary>

    static void Client()
    
{
      TcpChannel channel 
= new TcpChannel(802);
      ChannelServices.RegisterChannel(channel, 
false);
      RemotingConfiguration.RegisterWellKnownClientType(
typeof(Data), "tcp://localhost:801/data");

      Data data 
= new Data();
      data.OnTest 
+= new ClientData().OnTestMethod;
      data.Test();
    }


    
static void Main()
    
{
      Server();
      Client();
    }

  }

}