.NET Remoting 是 .NET Framework 早期的分布式通訊技術,支援跨應用程式域、跨程序、跨機器的對象調用。本文示範如何建立一個可按值封送的類別庫,並透過 TCP 通道在伺服器端註冊為 WellKnownObjectMode.SingleCall 模式的遠端物件,客戶端透過代理透明調用。
+----------------+ TCP 7777 +----------------+
| Client (WinForms) | <----------------> | Server (WinForms) |
| 引用 DLL.dll | 遠端代理調用 | 託管 api 物件 |
+----------------+ +----------------+
^ ^
| 共用介面/類型定義 (DLL.dll) |
+--------------------------------------+
test 及繼承 MarshalByRefObject 的遠端服務類別 api。// test.cs - 按值封送的資料物件(需 [Serializable])
using System;
namespace DLL
{
[Serializable]
public class test
{
public test() { }
public string Time => DateTime.Now.ToString();
}
}
// api.cs - 遠端服務物件(繼承 MarshalByRefObject,按引用封送)
using System;
using System.IO;
namespace DLL
{
public class api : MarshalByRefObject
{
public api() { }
public string GetTime() => new test().Time;
public string[] GetDirectorIEs(string path) => Directory.GetDirectories(path);
public string[] GetFiles(string path) => Directory.GetFiles(path);
}
}
關鍵點:
test 標記 [Serializable],跨界傳遞時會被「按值封送」(序列化副本傳至客戶端)。api 繼承 MarshalByRefObject,跨界傳遞的是「透明代理」,實際調用在伺服器端執行(按引用封送)。using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using DLL;
namespace Server
{
public class Form1 : System.Windows.Forms.Form
{
private void Form1_Load(object sender, EventArgs e)
{
// 1. 建立 TCP 通道監聽埠 7777
TcpChannel chan = new TcpChannel(7777);
ChannelServices.RegisterChannel(chan, false);
// 2. 註冊遠端類型為 SingleCall 模式(每次呼叫新實例)
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(DLL.api), // 服務類型
"test", // 服務 URI 片段
WellKnownObjectMode.SingleCall);
}
}
}
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using DLL;
namespace Client
{
public static class Global
{
public static string Host { get; set; } = "localhost";
public static int Port { get; set; } = 7777;
public static DLL.api ApiProxy
{
get
{
string url = $"tcp://{Host}:{Port}/test";
return (DLL.api)Activator.GetObject(typeof(DLL.api), url);
}
}
}
}
using System;
using System.Windows.Forms;
namespace Client
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// 註冊客戶端通道(無需指定埠,系統自動分配)
ChannelServices.RegisterChannel(new TcpChannel(), false);
Application.Run(new Form1());
}
}
public partial class Form1 : Form
{
private void Form1_Load(object sender, EventArgs e)
{
try
{
// 遠端呼叫:取得伺服器 C:\ 目錄下文件列表
string[] files = Global.ApiProxy.GetFiles(@"C:\");
// 繫結至 UI 控制項...
}
catch (Exception ex)
{
MessageBox.Show($"遠端調用失敗:{ex.Message}");
}
}
}
}
| 概念 | 說明 |
|---|---|
| 按值封送 | 物件完整序列化傳輸,接收端獲得獨立副本。適用於不可變資料、小型 DTO。需 [Serializable]。 |
| 按引用封送 | 傳遞代理,方法調用透過通道轉發至伺服器實體。適用於有狀態服務、大型物件、資源封裝。需繼承 MarshalByRefObject。 |
| SingleCall | 每次客戶端呼叫創建新服務實例,呼叫結束釋放。無狀態、可擴展、預設模式。 |
| Singleton | 所有客戶端共享單一實例。適合共享狀態、快取服務。 |
| 租用機制 | 遠端物件生命周期由租用控制,預設 5 分鐘,可覆寫 InitializeLifetimeService 返回 null 禁用回收。 |
[Serializable],且所有欄位皆可序列化;或實作 ISerializable 自定義邏輯。BeginInvoke/EndInvoke 或 Task.Factory.FromAsync 避免阻塞 UI 執行緒。.NET Remoting 屬於舊版技術(.NET Framework 專屬),.NET Core / .NET 5+ 不支援。新專案推薦:
本範例包含三個專案:DLL、Server、Client。編譯後先啟動 Server,再啟動 Client 即可觀察遠端調用效果。