Remoting 入门 - 最简单的有效示例






4.29/5 (27投票s)
2006年1月13日
9分钟阅读

68585

674
一个可用的远程处理示例。可直接用于您的项目中。
引言
更新于 2006年1月18日 - 我收到了关于本文的许多积极评价,以及一些要求在内容中添加描述性代码的请求。基于这些反馈,我已对文章进行了修改,提供了更详细的讨论——附带代码——关于远程服务器和客户端的设置。请享用!
我花了很多时间学习如何在我的应用程序中创建和使用基本的远程处理服务。Code Project 在这方面非常有帮助。尽管各种文章和教程提供了我所需的信息,但我从未找到一个简单、基础、可立即使用的远程处理示例。本文将尝试解决这个问题。
一旦您掌握了这里介绍的基础知识,就可以参考以下 Code Project 文章获取更详细的信息:
在 CodeProject 上还可以找到许多其他有趣的 es.
目标
为您提供一个非常简单、易于理解的、使用 .NET Remoting 的客户端/服务器系统实现。
一旦您理解了这段代码,就可以立即对其进行增强,用于您自己的应用程序。我省略了除设置和测试远程处理客户端和服务器所需的最少代码之外的所有内容。
使用代码
包含的 zip 文件包含一个 Visual Studio .NET 2003 解决方案,其中有三个项目。每个项目将在下面描述:
- 客户端 - 一个将使用解决方案创建的服务器的简单客户端。它通过 MainForm.cs 顶部的
#define
来支持与服务器的 TCP 和 HTTP 连接。 - 服务器 - 服务器应用程序。此应用程序提供两项服务。我想演示单个服务器应用程序如何为客户端提供多个独立的 es。与客户端一样,服务器可以使用 TCP 或 HTTP 连接,具体取决于 ServerStart.cs 文件中设置的
#define
。 - 接口 - 接口项目包含每个远程服务的接口。我选择接口作为解耦客户端和服务器的方法。由于所有远程服务器对象的实例化都通过接口进行,因此如果您更改了服务器实现,则无需重新构建客户端。
按照配置,您应该可以在本地计算机上构建解决方案并运行客户端和服务器。开始之前:
- 构建解决方案。
- 手动启动服务器应用程序。
- 手动启动客户端,或在调试器中运行客户端。
- 通过单击客户端窗体上的按钮来测试系统。
- 观察客户端和服务器窗口中显示的es。
让我们开始远程处理吧!
代码简单且注释良好。您应该查看它以了解整体情况。有几点需要仔细研究。
服务器端
在服务器端,您需要完成三个关键步骤才能使服务器正常工作。这些步骤是:
创建接口实现
让我们看看服务器项目中的 ICalculatorImplemention
类。它足够小,可以全部在此处显示:
public class ICalculatorImplementation: MarshalByRefObject,
ICalculator
{
/// <summary>
/// Default constructor
/// </summary>
public ICalculatorImplementation()
{
Console.WriteLine(
"ICalculatorImplementation constructor.");
}
#region ICalculator Members
public int Add(int A, int B)
{
Console.WriteLine("Calculator service: Add() called.");
return (A + B);
}
#endregion
} // class
最重要的事情是继承自 MarshalByRefObject
并实现您希望提供给客户端的接口。MarshalByRefObject
是 .NET 中远程处理的关键;它提供了跨应用程序域边界访问对象所需的支持——换句话说,就是访问运行在另一端(电线另一端,甚至您的机器上的另一个进程)的对象。对于像这样的简单服务,您可以基本忽略 MarshalByRefObject
。当您准备好学习更多内容时,值得阅读它。
创建托管和启动服务的应用程序
接下来,我们需要创建某种类型的应用程序来托管我们的服务。任何应用程序都适用;控制台、Windows 和 NT 服务在各种情况下都很有用。在此示例中,我使用项目向导创建了一个标准的 C# 控制台应用程序。所有工作都在应用程序入口点 static void Main()
中完成。
这是 ServerStart.cs 的摘录。为了清晰起见,我已删除了一些代码和注释:
[STAThread]
static void Main(string[] args)
{
// HTTP or TCP connection setup go here.
// See article section 3.
// Register the Http/TCP channel we created above.
ChannelServices.RegisterChannel(chan);
// Start Calculator service.
// Register calculator service
Type iCalc = Type.GetType(
"InterfaceTest.Server.ICalculatorImplementation");
// Configure the service
RemotingConfiguration.RegisterWellKnownServiceType(
iCalc,
"ICalcEndPoint",
WellKnownObjectMode.Singleton
);
}
我一会儿会看连接设置,所以现在代码的这一部分已被删除。一旦连接(TCP 或 HTTP)设置好,您就注册然后配置服务。上面的代码基本上是样板代码,您可以将自己的接口实现替换到上面的行中。
当您获取接口实现的类型时,您需要包含完整的命名空间以及类名才能获得正确的类型返回。在这种情况下,它是 InerfaceTest.Server.ICalculatorImplementation
。
RemotingConfiguration
类是一个 static
类,它提供了配置远程处理服务所需的所有方法。
当您超出此示例时,您将需要研究 WellKnownObjectMode
枚举。此枚举控制您的服务如何启动以及如何在客户端之间共享。
Singleton
模式表示将启动 ICalculatorImplemention
的单个实例,它将在所有发出请求的客户端之间共享。如果您希望您的服务是无状态的并为每个客户端调用提供一个实例,则可以使用 WellKnownObjectMode.SingeCall
。
注意:您可以在单个应用程序中托管多个服务。提供的示例代码通过托管 ICalculatorImplemention
和 ITestImplementation
类作为服务来演示这一点。这两个服务共享相同的通道、端口和通信方法(TCP 或 HTTP)。每个服务 URL 都以其终结点而不同。
例如,示例代码中的计算器服务 URL 是:
TCP://:65101/ICalcEndPoint
而测试服务的 URL 是:
TCP://:65101/ITestEndPoint
设置 TCP 或 HTTP 通信通道
远程处理可以使用 HTTP 或 TCP 进行通信。HTTP 设置非常简单。以下是一个远程处理服务器的示例:
HttpChannel chan = new HttpChannel(65101);
就是这样!创建一个 HttpChannel
实例并设置一个端口号——在本例中为 65101。
TCP 通道在客户端和服务器上设置更复杂。取决于您的应用程序需求,性能优势可能值得您付出努力。TCP 远程处理比 HTTP 远程处理更快。
以下是如何在服务器上创建 TCP 通道。有关详细信息,请参阅 ServerStart.cs:
BinaryClientFormatterSinkProvider clientProvider = null;
BinaryServerFormatterSinkProvider serverProvider =
new BinaryServerFormatterSinkProvider();
serverProvider.TypeFilterLevel =
System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
IDictionary props = new Hashtable();
props["port"] = 65101;
props["typeFilterLevel"] =
System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
TcpChannel chan =
new TcpChannel(props, clientProvider, serverProvider);
尽管 TCP 设置需要更多步骤,但您可以将此代码用作样板。只需将 props["port"] = 65101
替换为您选择的端口即可。
客户端
在客户端,当您想访问远程服务时,您需要做两件事。它们是:
创建到远程服务的通道
与服务器一样,您需要创建一个 TCP 或 HTTP 通道。步骤几乎相同。
对于 HTTP,语句:HttpChannel chan = new HttpChannel(0);
就足够了。请注意,您没有指定端口号。由于这是一个客户端,因此不需要。
TCP 代码与服务器也很相似。以下是客户端项目 MainForm.cs 的摘录:
url = @"tcp://:65101/";
BinaryClientFormatterSinkProvider clientProvider =
new BinaryClientFormatterSinkProvider();
BinaryServerFormatterSinkProvider serverProvider =
new BinaryServerFormatterSinkProvider();
serverProvider.TypeFilterLevel =
System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
IDictionary props = new Hashtable();
props["port"] = 0;
props["name"] = System.Guid.NewGuid().ToString();
props["typeFilterLevel"] =
System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
TcpChannel chan =
new TcpChannel(props, clientProvider, serverProvider);
与服务器代码不同,这段代码创建了 BinartClient
和 BinaryServer
接收方提供程序。与 HTTP 示例一样,它将端口号设置为 0。
注册并连接
创建通信通道后,您注册通道,使用 MarshalByRefObject
设置对服务器的远程访问,并创建您希望使用的远程类的本地实例。此摘录演示了如何获取和使用 ICalculatorImplementation
的实例:
//
// Register the channel
//
ChannelServices.RegisterChannel(chan);
//
// Setup remote access to ICalc implementation
//
MarshalByRefObject calc =
(MarshalByRefObject)RemotingServices.Connect(
typeof(Interfaces.ITest), url + "ICalcEndPoint"
);
// Set a reference to the service.
calcReference = calc as Interfaces.ICalculator;
// Test
int test = calcReference.Add(10,20);
Console.WriteLine("Add(10,20) returned " + test.ToString());
创建 calcReference
后,您就可以像使用 ICalculatorImplementation
的普通实例一样使用它。
结论
这是我能创建的最简单的远程处理示例。代码注释良好,希望您在熟悉远程处理概念的同时可以使用它。上面的部分重点介绍了最重要的远程处理步骤。下面我列出了一些您在处理远程处理时也应该考虑的关键点和感兴趣的领域。
接口
如果您不熟悉使用接口,我建议您回顾一下该主题。接口是我用来向客户端提供远程服务的方法,据我所知,这是最常用的方法。
接口允许您将某个类的远程实例视为本地对象;它还将客户端与服务器解耦。
您会注意到我创建了一个单独的接口类库来保存客户端和服务器应用程序共享的所有接口。简单来说,这意味着更改客户端或服务器的实现不会强制您重新构建另一个。
接口也提供透明性。一旦您创建了到服务器的通信通道并创建了您想使用的服务的接口实例,您的客户端代码就可以完全透明地使用远程对象。
HTTP 与 TCP 连接
当您检查代码时,您会发现 HTTP 连接到目前为止是最简单的设置。但这也有一些代价。HTTP 传输比 TCP/Binary 连接慢得多。优点是 HTTP 可以穿过防火墙,并在需要时提供内置的安全机制。它还兼容非 .NET 代码。
TCP 连接更复杂,但速度更快,并且只能与 .NET 应用程序兼容。
关于 TCP 和 HTTP 远程处理,Code Project 和其他网站上提供了大量信息。一旦您掌握了基础知识,就应该花一些时间阅读这些文章。请参阅本文概述部分提供的链接。
错误处理
为了简化代码,我省略了所有错误处理。在实际应用程序中,您至少应该在客户端添加一些异常处理。
许多实际应用程序会将对远程方法的每次调用包装在 try
/catch
块中。我通常不这样做。相反,我将 try
/catch
块放在创建服务引用的客户端代码周围。
我的服务器总是有一个 Init()
或 Test()
方法,客户端可以使用它来验证通信通道和服务是否正常工作。我将我的 Test()
调用包装在 try
/catch
中,并将任何异常报告给用户。
我发现这种技术使代码更易于阅读并提高了性能。但是,我的方法不一定是最好的方法,因此请自行承担风险使用。