.NET Remoting 全面教程
目录
- 什么是 .NET Remoting?
- 核心概念
- 为什么现在不推荐使用 .NET Remoting?
- 简单示例:创建一个 "Hello World" Remoting 应用
- 步骤 1: 定义远程对象 (Marshal-By-Value 对象)
- 步骤 2: 创建服务器端
- 步骤 3: 创建客户端
- 步骤 4: 运行和测试
- 进阶概念
- Marshal-By-Reference (MBR) 对象
- 生命周期管理
- 自定义信道
- 与 WCF 和 gRPC 的对比
- 总结与学习资源
什么是 .NET Remoting?
.NET Remoting 是微软在 .NET Framework 早期(.NET 1.0/1.1/2.0/3.0/3.5)推出的一种跨应用程序域的通信技术,它的主要目标是允许不同进程(甚至在同一台机器的不同计算机上)的应用程序像调用本地对象一样相互调用方法。

你可以把它想象成一个“超级 DCOM”,它提供了比 DCOM 更灵活、更强大的功能,并且完全基于 .NET 的类型系统。
核心概念
要理解 .NET Remoting,必须掌握以下几个核心组件:
a. 远程对象
这是客户端想要调用的对象,它必须继承自 MarshalByRefObject 类,或者可以被序列化(实现 ISerializable 或标记为 [Serializable])。
- Marshal-By-Reference (MBR): 对象本身不离开服务器,当客户端调用其方法时,实际上是调用一个代理对象上的方法,这个代理通过网络将请求发送到服务器上的真实对象,这就像一个遥控器,你按按钮,但真正的电视在另一个房间里。
- Marshal-By-Value (MBV): 对象被完整地序列化(转换成字节流),然后从服务器发送到客户端,客户端得到的是这个对象的副本,之后的所有调用都在本地副本上进行,不再与服务器交互,这就像把一本书从图书馆借回家,你可以在家里阅读,不需要每次都跑回图书馆。
b. 信道
信道是远程对象传输数据的通道,它负责将方法调用序列化并通过网络发送,同时也将返回结果反序列化。

- .NET Remoting 提供了两种内置信道:
TcpChannel: 使用 TCP 协议,性能更高,更适合局域网或对性能要求高的场景。HttpChannel: 使用 HTTP 协议,可以穿透防火墙,更适合互联网场景。
- 信道可以配置使用不同的格式化程序 来序列化消息,
BinaryFormatter(性能高)或SoapFormatter(基于 XML,可读性好,但性能较低)。
c. 激活
激活是指客户端获取远程对象代理的过程。.NET Remoting 提供了两种激活方式:
- 服务器端激活: 也称为“WellKnown”激活,服务器预先注册好一个类型,客户端通过一个众所周知的 URI(统一资源标识符)来请求该类型的实例,服务器会为每个客户端请求创建一个新的实例。
SingleCall: 每次客户端调用都会创建一个新的对象实例,调用结束后立即销毁,无状态。Singleton: 整个应用程序域中只有一个对象实例,所有客户端共享这个实例,有状态。
- 客户端激活: 客户端通过
new关键字或Activator.CreateInstance来创建远程对象,服务器会为每个客户端创建一个专有的对象实例,并管理其生命周期。
d. 应用程序域
.NET Remoting 的一个强大之处在于它可以在同一个进程内创建多个应用程序域,每个 AppDomain 都像一个轻量级的进程,拥有独立的内存空间和加载的程序集。.NET Remoting 允许这些 AppDomain 之间像跨进程通信一样互相调用对象,而无需操作系统级别的进程间通信开销。
为什么现在不推荐使用 .NET Remoting?
这是一个非常重要的问题。.NET Remoting 是一项过时的技术。
- 安全性问题:
BinaryFormatter存在严重的安全漏洞,容易被利用进行远程代码执行攻击,微软官方已不再推荐使用它。 - 复杂性: 配置和使用相对复杂,容易出错。
- 平台限制: 它是 .NET Framework 的专有技术,无法在 .NET Core 或 .NET 5+ 上运行,而现代的 .NET 是跨平台的。
- 有更好的替代品:
- WCF (Windows Communication Foundation): .NET Framework 的后继者,功能更强大、更安全、更灵活,支持多种协议和绑定。
- ASP.NET Web API: 专注于构建 RESTful 服务,简单易用,是构建 HTTP 服务的首选。
- gRPC: 现代高性能的 RPC 框架,基于 HTTP/2 和 Protocol Buffers,适合微服务架构。
除非你正在维护一个非常古老的 .NET Framework 应用程序,否则绝对不要在新项目中使用 .NET Remoting,本教程仅用于学习和理解历史技术。

简单示例:创建一个 "Hello World" Remoting 应用
我们将创建一个简单的控制台应用程序,包含三个项目:
SharedLibrary: 包含共享的远程对象接口和实现。Server: 服务器端,发布远程对象。Client: 客户端,调用远程对象的方法。
步骤 1: 定义远程对象 (Marshal-By-Value 对象)
这是一个最简单的例子,我们使用 MBV 对象。
-
创建一个新的类库项目
SharedLibrary。 -
添加一个接口
IGreetingService.cs:// SharedLibrary/IGreetingService.cs using System; namespace SharedLibrary { // 必须标记为 [Serializable] 才能被 MBV 传递 [Serializable] public interface IGreetingService { string Greet(string name); } } -
添加一个实现类
GreetingService.cs:// SharedLibrary/GreetingService.cs namespace SharedLibrary { // 实现接口 public class GreetingService : IGreetingService { public string Greet(string name) { Console.WriteLine($"[服务器端] 正在为 '{name}' 生成问候语..."); return $"你好, {name}! 欢迎使用 .NET Remoting!"; } } } -
将
SharedLibrary的输出路径设置为与Server和Client相同,以便复制 DLL 文件,或者在生成后手动复制。
步骤 2: 创建服务器端
-
创建一个新的控制台应用程序项目
Server。 -
添加对
SharedLibrary项目的引用。 -
编写
Program.cs:// Server/Program.cs using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using SharedLibrary; namespace Server { class Program { static void Main(string[] args) { Console.WriteLine("服务器启动中..."); // 1. 创建并注册 TCP 信道 // 参数:端口号 和 格式化程序提供者 TcpChannel channel = new TcpChannel(8080); ChannelServices.RegisterChannel(channel, false); // 2. 注册远程对象 // 参数:对象在客户端访问的 URI ("remote") 和 对象类型 RemotingConfiguration.RegisterWellKnownServiceType( typeof(GreetingService), "remote", WellKnownObjectMode.Singleton); // 使用 Singleton 模式 Console.WriteLine("服务器已启动,等待客户端连接..."); Console.WriteLine("按任意键退出..."); Console.ReadKey(); } } }
步骤 3: 创建客户端
-
创建一个新的控制台应用程序项目
Client。 -
添加对
SharedLibrary项目的引用。 -
编写
Program.cs:// Client/Program.cs using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using SharedLibrary; namespace Client { class Program { static void Main(string[] args) { Console.WriteLine("客户端启动中..."); // 1. 创建并注册 TCP 信道 // 客户端只需要提供格式化程序提供者 TcpChannel channel = new TcpChannel(); ChannelServices.RegisterChannel(channel, false); // 2. 激活远程对象 // 参数:服务器地址 ("tcp://localhost:8080/remote") 和 对象类型 IGreetingService service = (IGreetingService)Activator.GetObject( typeof(IGreetingService), "tcp://localhost:8080/remote"); // 3. 调用远程方法 if (service != null) { string greeting = service.Greet("张三"); Console.WriteLine($"[客户端] 收到来自服务器的问候: {greeting}"); } else { Console.WriteLine("无法连接到远程对象。"); } Console.WriteLine("按任意键退出..."); Console.ReadKey(); } } }
步骤 4: 运行和测试
- 先启动服务器:运行
Server项目,控制台会显示“服务器已启动...”。 - 再启动客户端:运行
Client项目。 - 观察输出:
- 服务器控制台 会显示:
[服务器端] 正在为 '张三' 生成问候语... - 客户端控制台 会显示:
[客户端] 收到来自服务器的问候: 你好, 张三! 欢迎使用 .NET Remoting!
- 服务器控制台 会显示:
这证明了客户端成功调用了服务器上的方法。
进阶概念
a. Marshal-By-Reference (MBR) 对象
对于 MBR 对象,服务器端的代码需要稍作修改,并且客户端的调用方式也不同。
服务器端修改 (Server 项目):
// SharedLibrary/MBRGreetingService.cs
using System; // 必须添加这个 using
namespace SharedLibrary
{
// 必须继承 MarshalByRefObject
public class MBRGreetingService : MarshalByRefObject
{
public MBRGreetingService()
{
Console.WriteLine("[服务器] MBR 对象被构造!");
}
public string Greet(string name)
{
// 每次调用都会打印,证明是同一个对象(Singleton)
Console.WriteLine($"[服务器端] MBR 对象正在为 '{name}' 生成问候语...");
return $"你好, {name}! (来自 MBR 对象)";
}
}
}
// Server/Program.cs (修改部分)
// ... 其他代码不变
// 注册 MBR 对象
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(MBRGreetingService),
"mbr_remote",
WellKnownObjectMode.Singleton); // 仍然使用 Singleton
客户端修改 (Client 项目):
// Client/Program.cs (修改部分)
// ... 其他代码不变
// 激活 MBR 对象
// 注意:这里也是 Activator.GetObject
MBRGreetingService mbrService = (MBRGreetingService)Activator.GetObject(
typeof(MBRGreetingService),
"tcp://localhost:8080/mbr_remote");
if (mbrService != null)
{
// 调用方法
string greeting1 = mbrService.Greet("李四");
Console.WriteLine($"[客户端] 第一次调用结果: {greeting1}");
string greeting2 = mbrService.Greet("王五");
Console.WriteLine($"[客户端] 第二次调用结果: {greeting2}");
}
当你运行这个 MBR 示例时,你会发现服务器端只打印了一次“MBR 对象被构造!”,但两次方法调用都打印了方法内的日志,这证明了客户端一直在使用同一个服务器端对象实例。
b. 生命周期管理
对于 MBR 对象,.NET Remoting 提供了 Lease-Based 生命周期管理,每个远程对象都有一个“租约”(Lease),租约到期后如果没有被续订,对象就会被垃圾回收。
- 初始租约时间: 对象创建后存在的时间。
- 租约管理器: 负责跟踪所有租约。
- 续订: 客户端可以主动续订租约,保持对象活跃。
这个机制比较复杂,在实际应用中需要仔细配置。
c. 自定义信道
你可以实现自己的 IChannelReceiver 和 IChannelSender 接口来创建自定义信道,例如通过命名管道、消息队列等进行通信,但这在 .NET Remoting 中非常罕见。
与 WCF 和 gRPC 的对比
| 特性 | .NET Remoting | WCF | gRPC |
|---|---|---|---|
| 时代 | 过时 | 较新,但已被标记为“维护中” | 现代 |
| 平台 | 仅限 .NET Framework | .NET Framework, .NET Core, .NET 5+ | 跨平台 |
| 安全性 | 弱,BinaryFormatter 有漏洞 |
强,支持多种安全标准 | 强,基于 TLS |
| 协议 | TCP, HTTP | HTTP, TCP, Named Pipes, MSMQ 等 | HTTP/2, gRPC |
| 数据格式 | Binary, SOAP | XML, JSON, Binary | Protocol Buffers |
| 主要用途 | 跨 AppDomain 通信 | 企业级服务(SOA) | 微服务,高性能 RPC |
| 配置 | 复杂,代码或配置文件 | 灵活,代码或配置文件 | 简单,基于 Protobuf 文件 |
| 推荐度 | 不推荐 | 维护中,新项目建议用 gRPC 或 Web API | 推荐 |
总结与学习资源
.NET Remoting 是 .NET 生态系统中一个重要的历史里程碑,它首次提供了强大而灵活的分布式通信能力,由于其固有的安全风险、平台限制和复杂性,它已经被更现代、更安全、更通用的技术(如 WCF 和 gRPC)所取代。
学习资源:
- 官方文档 (历史): Microsoft Docs - .NET Remoting (这些文档可能已不再更新)。
- 书籍: 《.NET Framework 高级编程》(早期版本)中有详细的章节介绍。
- 博客文章: 搜索 "dotnet remoting tutorial" 可以找到许多经典的英文教程,CodeProject 上的文章。
希望这份教程能帮助你全面地了解 .NET Remoting。
