.NET Remoting 是早期 .NET Framework 提供的分散式物件通訊基礎設施,雖然已被 WCF/gRPC 取代,但其核心概念——應用程式域隔離、通道與格式化器、激活模式、序列化邊界——對理解現代微服務架構、Actor 模型、甚至 Blazor WebAssembly 的 JS 互操作仍具啟發意義。本文深度解析 Remoting 兩種傳遞模式(ByRef/ByValue),並實作一個功能完整的分散式任務伺服器。
┌─────────────┐ TCP Channel (8085) ┌─────────────┐
│ Client │ ◄─────────────────────────► │ Server │
│ AppDomain │ 1. Register Channel │ AppDomain │
│ │ 2. Activator.GetObject │ │
│ - ITask │ 3. Proxy → Remote Call │ - TaskRunner (MarshalByRefObject) │
│ - ClientTask (Serializable) │ - Singleton │
└─────────────┘ └─────────────┘
▲ │
│ ByValue: Serialize entire object │
│ ByRef: Proxy → remote invocation │
└────────────────────────────────────────────┘
| 維度 | ByRef (MarshalByRefObject) | ByValue ([Serializable]) |
|---|---|---|
| 物件實際位置 | 永遠駐留在伺服器 AppDomain | 序列化後傳輸至客戶端 AppDomain |
| 方法執行端 | 伺服器端 | 客戶端本地 |
| 網路往返 | 每次呼叫皆跨網路 | 僅傳輸時跨網路 |
| 狀態共享 | 天然共享 (Singleton) | 各客戶端擁有獨立副本 |
| 適用場景 | 資源密集型服務 (DB連線、硬體控制) | 計算密集型任務、資料傳輸物件 (DTO) |
| 序列化需求 | 僅 Proxy 需序列化 | 整個物件圖必須可序列化 |
namespace TaskServer
{
public interface ITask
{
object Run(); // 執行核心邏輯,回傳結果物件
string Identify(); // 任務描述,供日誌/監控使用
}
}
namespace TaskServer
{
public class TaskRunner : MarshalByRefObject
{
private ITask _remoteTask;
public string LoadTask(ITask task)
{
if (task == null) return "[e] Task reference is NULL.";
_remoteTask = task;
Console.WriteLine($"[i] Task loaded: {_remoteTask.Identify()}");
return "[i] Task loaded. Welcome to the All Powerful TaskServer.";
}
public object RunTask()
{
Console.WriteLine("[i] Running task on server...");
object result = _remoteTask.Run();
Console.WriteLine("[i] Task finished.");
return result; // 結果物件需 [Serializable] 才能回傳給客戶端
}
}
}
public class TaskServerEngine
{
public static void Main()
{
Console.WriteLine("The All Powerful Task Server!");
TcpChannel chan = new TcpChannel(8085);
ChannelServices.RegisterChannel(chan, false); // false = 不加密
Type runnerType = typeof(TaskRunner);
RemotingConfiguration.RegisterWellKnownServiceType(
runnerType, "TaskServer", WellKnownObjectMode.Singleton);
Console.WriteLine("[i] Task Server started on tcp://localhost:8085/TaskServer");
Console.ReadLine(); // 保持執行
}
}
[Serializable]
public class ClientTask : ITask
{
private int _a, _b;
public ClientTask(int a, int b) { _a = a; _b = b; }
public object Run() => _a * _b; // 複雜計算邏輯放這裡
public string Identify() => $"MultiplyTask: {_a} × {_b}";
}
public class Client
{
public static void Main()
{
TcpChannel chan = new TcpChannel();
ChannelServices.RegisterChannel(chan, false);
TaskRunner runner = (TaskRunner)Activator.GetObject(
typeof(TaskRunner), "tcp://localhost:8085/TaskServer");
ClientTask task = new ClientTask(100, 100);
string loadMsg = runner.LoadTask(task); // ByValue 傳遞任務物件
Console.WriteLine($"Server: {loadMsg}");
object result = runner.RunTask(); // 遠端執行,結果 ByValue 回傳
Console.WriteLine($"Result: {result}"); // 10000
}
}
csc /target:library /out:ITask.dll ITask.cs
csc /target:library /out:TaskRunner.dll TaskRunner.cs /r:ITask.dll
csc /out:TaskServer.exe TaskServerEngine.cs /r:ITask.dll /r:TaskRunner.dll
csc /out:TaskClient.exe Client.cs /r:ITask.dll /r:TaskRunner.dll
// ITask 擴展
public interface IAsyncTask : ITask
{
IAsyncResult BeginRun(AsyncCallback callback, object state);
object EndRun(IAsyncResult ar);
}
// 客戶端不阻塞等待
IAsyncResult ar = runner.BeginRunTask(callback, null);
PriorityQueue<ITask>TaskRunner (SingleCall 模式) 形成 Worker Poolnew TcpChannel(8085, null, new BinaryServerFormatterSinkProvider { TypeFilterLevel = TypeFilterLevel.Full })IServerChannelSinkProvider 實作 Token 驗證BinaryFormatter.Binder = new SafeSerializationBinder()| Remoting 概念 | 現代對應技術 | 關鍵差異 |
|---|---|---|
| AppDomain 隔離 | Process/Container 隔離 | 更強隔離,跨機器部署 |
| MarshalByRefObject | gRPC Service / Web API Controller | 契約優先,語言中立 |
| [Serializable] ByValue | Protobuf/JSON DTO | 版本容忍,跨平台 |
| Singleton/SingleCall | Singleton/Scoped/Transient (DI) | 生命週期由容器管理 |
| BinaryFormatter | System.Text.Json / Protobuf | 安全、高效、標準化 |
.NET Remoting 雖已退役,但其「透明遠端呼叫」與「物件遷移語意」的設計哲學,深刻影響了後續的 WCF、.NET Remoting (Core 3.0+ 移除)、gRPC、Orleans Actor 框架。掌握 ByRef/ByValue 邊界、序列化陷阱、激活模式選擇,能幫助開發者在微服務拆分、分散式任務佇列、邊緣計算等現代場景中做出正確的架構決策。本文實作的任務伺服器雖簡單,卻完整展示了「計算遷移至資源端」的分散式計算核心價值。