跟我一起学WCF(2)——利用.NET Remoting技术开发分布式应用

一、引言

  上一篇博文分享了消息队列(MSMQ)技术来实现分布式应用,在这篇博文继续分享下.NET平台下另一种分布式技术——.NET Remoting。

二、.NET Remoting 介绍

2.1 .NET Remoting简介

  .NET REmoting与MSMQ不同,它不支持离线可得,另外只适合.NET平台的程序进行通信。它提供了一种允许对象通过应用程序域与另一个对象进行交互的框架。.NET 应用程序都在一个主应用程序域中执行的,在一个应用程序域中的代码不能访问另一个应用程序域的数据,然而在某些情况下,我们需要跨应用程序域,与另外的应用程序域进行通信,这时候就可以采用.NET Remoting技术来实现与另一个程序域中的对象进行交互。

2.2 .NET Remoting基本原理

  .NET Remoting技术是通过通道来实现两个应用程序之间对象的通信的。首先,客户端通过Remoting技术,访问通道来获得服务器端对象,再通过代理解析为客户端对象,也称作透明代理,此时获得客户端对象只是服务器对象的一个引用。这既保证了客户端和服务端有关对象的松散耦合,同时优化了通信的性能。在这个过程中,当客户端通过透明代理来调用远程对象的方法时,此时会将调用封装到一个消息对象中,该消息对象包括远程对象信息,被调用的方法名和参数,然后透明代理会将调用委托给真实代理(RealProxy对象)的Invoke方法来生成一个IMethodCallMessage,接着通过序列化把这个消息对象序列化成数据流发送到通道,通道会把数据流传送到服务器端。当服务器接收到经过格式化的数据之后,首先从中通过反序列化来还原消息对象,之后在服务器端来激活远程对象,并调用对应的方法,而方法的返回结果过程则是按照之前的方法反向重复一遍,具体的实现原理图如下所示:

2.3 .NET Remoting几个重要概念

  上面简单介绍了下.NET Remoting实现分布式应用程序的基本原理,这里介绍下在.NET Remoting中涉及的几个重要概念。

  1. 远程对象:是运行在服务器端的对象,客户端不能直接调用,由于.NET Remoting传递的对象是以引用的方式,因此所传递的远程对象必须继承MarshalByRefObject类,这个类可以使远程对象在.NET Remoting应用通信中使用,支持对象的跨域边界访问。
  2. 远程对象的激活方式:在访问服务器端的一个对象实例之前,必须通过一个名为Activation的进程创建它并进行初始化。这种客户端通过通道来创建远程对象的方式称为对象的激活。在.NET Remoting中,远程对象的激活分为两大类:服务器端激活和客户端激活。
  • 服务器端激活,又叫做WellKnow(知名对象)激活模式,为什么称为知名对象激活模式呢?是因为服务应用程序在激活对象实例之前会在一个众所周知的统一资源标示符(URI)上发布这个类型,然后该服务器进行会为此类型配置一个WellKnow对象,并根据指定的端口或地址来发布对象。.NET Remoting把服务器端激活又分为SingleTon模式和SingleCall模式两种。

  SingleTon模式:此为有状态模式。如果设置为SingleTon激活模式,则.NET Remoting将为所有客户端建立同一个对象实例。当对象处于活动状态时,SingleTon实例会处理所有后来的客户端访问请求,而不管它们是同一个客户端,还是其他客户端。SingleTon实例将在方法调用中一直维护其状态,类似static成员的概念

  SingleCall模式:是一种无状态模式。一旦设置为SingleCall模式,则当客户端调用远程对象的方法时,Remoting会为每一个客户端建立一个远程对象实例,对象实例的销毁则是由GC自动管理。类似实例成员的概念。

  • 客户端激活:与Wellknow模式不同,。NET Remoting在激活每个对象实例的时候,会给每个客户端激活的类型指派一个URI。客户端激活模式一旦获得客户端的请求,将为每一个客户端都建立一个实例引用。SingleCall模式与客户端激活模式的区别有:首先,对象实例创建的时间不同。客户端激活方式是客户一旦发出调用请求就实例化,而SingleCall则要等到调用对象方法时再创建。其次,SingleCall模式激活的对象是无状态的,对象声明周期由GC管理,而客户端激活的对象是有状态的,其生命周期可自定义。第三,两种激活模式在服务器端和客户端实现的方法不一样,尤其是在客户端,SingleCall模式由GetObject()来激活,它调用对象默认的构造函数,而客户端激活模式,则通过CreateInstance()来激活,它可以传递参数,所以可以调用自定义的构造函数来创建实例。

  3. 通道:在.NET Remoting中时通过通道来实现两个应用程序域之间对象的通信。.NET Remoting中包括4中通道类型:

  1. TcpChannel:Tcp通道使用Tcp协议来跨越.Net Remoting边界来传输序列化的消息流,TcpChannel默认使用二进制格式序列化消息对象,因此具有更高的传输性能,但不提供任何内置的安全功能。
  2. HttpChannel:Http通道使用Http协议在客户端和服务器之间发生消息,使其在Internet上穿越防火墙来传输序列化的消息流(这里准确讲不能说穿越,主要是因为防火墙都开放了80端口,所以使用Http协议可以穿过防火墙进行数据的传输,如果防火墙限制了80端口,Http协议也照样不能穿越防火墙)。默认情况下,HttpChannel使用Soap格式序列化消息对象,因此它具有更好的互操作性,并且可以使用Http协议中的加密机制来对消息进行加密来保证安全性。因此,通常在局域网内,我们更多地使用TcpChannel,如果要穿越防火墙,则使用HttpChannel。
  3. IpcChannel:进程间通信,只使用同一个系统进程之间的通信,不需要主机名和端口号。而使用Http通道和Tcp通道都要指定主机名和端口号。
  4. 自定义通道:自定义的传输通道可以使用任何基本的传输协议来进行通信,如UDP协议、SMTP协议等。

三、利用.NET Remoting技术开发分布式应用三部曲

  前面详细介绍了.NET Remoting相关内容,下面具体看看如何使用.NET Remoting技术来开发分布式应用程序。开发.NET Remoting应用分三步走。

第一步:创建远程对象,该对象必须继承MarshalByRefObject对象。具体的示例代码如下:

 1 namespace RemotingObject
 2 {
 3     // 第一步:创建远程对象
 4     // 创建远程对象——必须继承MarshalByRefObject,该类支持对象的跨域边界访问
 5     public class MyRemotingObject :MarshalByRefObject
 6     {
 7         // 用来测试Tcp通道
 8         public int AddForTcpTest(int a, int b)
 9         {
10             return a + b;
11         }
12
13         // 用来测试Http通道
14         public int MinusForHttpTest(int a, int b)
15         {
16             return a - b;
17         }
18
19         // 用来测试IPC通道
20         public int MultipleForIPCTest(int a, int b)
21         {
22             return a * b;
23         }
24     }
25 }

  远程对象分别定义3个方法,目的是为了测试3中不同的通道方式的效果。

第二步:创建服务器端,需要添加System.Runtime.Remoting.dll引用,具体实现代码如下所示:

 1 using System;
 2 using System.Runtime.Remoting;
 3 using System.Runtime.Remoting.Channels;
 4 using System.Runtime.Remoting.Channels.Http;
 5 using System.Runtime.Remoting.Channels.Ipc;
 6 using System.Runtime.Remoting.Channels.Tcp;
 7
 8 namespace RemotingServerHost
 9 {
10     // 第二步:创建宿主应用程序
11     class Server
12     {
13         static void Main(string[] args)
14         {
15             // 1.创建三种通道
16
17             // 创建Tcp通道,端口号9001
18             TcpChannel tcpChannel = new TcpChannel(9001);
19
20             // 创建Http通道,端口号9002
21             HttpChannel httpChannel = new HttpChannel(9002);
22
23             // 创建IPC通道,端口号9003
24             IpcChannel ipcChannel = new IpcChannel("IpcTest");
25
26             // 2.注册通道
27             ChannelServices.RegisterChannel(tcpChannel, false);
28             ChannelServices.RegisterChannel(httpChannel, false);
29             ChannelServices.RegisterChannel(ipcChannel, false);
30
31             // 打印通道信息
32             // 打印Tcp通道的名称
33             Console.WriteLine("The name of the TcpChannel is {0}", tcpChannel.ChannelName);
34             // 打印Tcp通道的优先级
35             Console.WriteLine("The priority of the TcpChannel is {0}", tcpChannel.ChannelPriority);
36
37             Console.WriteLine("The name of the HttpChannel is {0}", httpChannel.ChannelName);
38             Console.WriteLine("The priority of the httpChannel is {0}", httpChannel.ChannelPriority);
39
40             Console.WriteLine("The name of the IpcChannel is {0}", ipcChannel.ChannelName);
41             Console.WriteLine("The priority of the IpcChannel is {0}", ipcChannel.ChannelPriority);
42
43             // 3. 注册对象
44             // 注册MyRemotingObject到.NET Remoting运行库中
45             RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemotingObject.MyRemotingObject), "MyRemotingObject", WellKnownObjectMode.Singleton);
46             Console.WriteLine("Press any key to exit");
47             Console.ReadLine();
48         }
49     }
50 }

第三步:创建客户端程序,具体的实现代码如下所示:

 1 using RemotingObject;
 2 using System;
 3
 4 namespace RemotingClient
 5 {
 6     class Client
 7     {
 8         static void Main(string[] args)
 9         {
10             // 使用Tcp通道得到远程对象
11             //TcpChannel tcpChannel = new TcpChannel();
12             //ChannelServices.RegisterChannel(tcpChannel, false);
13             MyRemotingObject proxyobj1 = Activator.GetObject(typeof(MyRemotingObject), "tcp://localhost:9001/MyRemotingObject") as MyRemotingObject;
14             if (proxyobj1 == null)
15             {
16                 Console.WriteLine("连接TCP服务器失败");
17             }
18
19             //HttpChannel httpChannel = new HttpChannel();
20             //ChannelServices.RegisterChannel(httpChannel, false);
21             MyRemotingObject proxyobj2 = Activator.GetObject(typeof(MyRemotingObject), "http://localhost:9002/MyRemotingObject") as MyRemotingObject;
22             if (proxyobj2 == null)
23             {
24                 Console.WriteLine("连接Http服务器失败");
25             }
26
27             //IpcChannel ipcChannel = new IpcChannel();
28             //ChannelServices.RegisterChannel(ipcChannel, false);
29             MyRemotingObject proxyobj3 = Activator.GetObject(typeof(MyRemotingObject), "ipc://IpcTest/MyRemotingObject") as MyRemotingObject;
30             if (proxyobj3 == null)
31             {
32                 Console.WriteLine("连接Ipc服务器失败");
33             }
34             // 输出信息
35             Console.WriteLine("This call object by TcpChannel, 100 + 200 = {0}", proxyobj1.AddForTcpTest(100, 200));
36             Console.WriteLine("This call object by HttpChannel, 100 - 200 = {0}", proxyobj2.MinusForHttpTest(100, 200));
37             Console.WriteLine("This call object by IpcChannel, 100 * 200 = {0}", proxyobj1.MultipleForIPCTest(100, 200));
38             Console.WriteLine("Press any key to exit!");
39             Console.ReadLine();
40         }
41     }
42 }

  经过上面的三步,我们就完成了这个分布式应用的开发工作,下面测试下该程序是否可以正常运行,首先,运行服务器端,你将看到如下界面:

  在.NET Remoting中,是允许同时创建多个通道的,但是.NET Remoting要求通道的名字必须不同,因为名字是用来标识通道的唯一标识符。但上面代码中,我们并没有指明通道的名字,为什么还可以允许成功呢?从上面图片可知,当我们创建通道时,如果没有为其显式指定通道名,则会使用对应的通道类型作为该通道名,如TcpChannel将会以tcp作为通道名,如果想注册多个Tcp通道则必须显式指定其名字。

  下面看看运行客户端所获得的结果,具体客户端运行效果如下图所示:

四、使用配置文件来重写上面的分布式程序

  在第三部分中,我们是把服务器的各种通道方式和地址写死在程序中的,这样的实现方式部署起来不方便,下面使用配置文件的方式来配置服务器端的通道类型和服务器地址。  远程对象的定义不需要改变,下面直接看服务器端使用配置文件后的实现代码如下所示:

 1 using System;
 2 using System.Runtime.Remoting;
 3 using System.Runtime.Remoting.Channels;
 4
 5 namespace RemotingServerHostByConfig
 6 {
 7     class Program
 8     {
 9         static void Main(string[] args)
10         {
11             RemotingConfiguration.Configure("RemotingServerHostByConfig.exe.config", false);
12
13             foreach (var channel in ChannelServices.RegisteredChannels)
14             {
15                 // 打印通道的名称
16                 Console.WriteLine("The name of the Channel is {0}", channel.ChannelName);
17                 // 打印通道的优先级
18                 Console.WriteLine("The priority of the Channel is {0}", channel.ChannelPriority);
19             }
20             Console.WriteLine("按任意键退出……");
21             Console.ReadLine();
22         }
23     }
24 }

  服务端的配置文件的内容为:

 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <!--服务端App.config的内容-->
 3 <configuration>
 4     <startup>
 5         <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
 6     </startup>
 7   <system.runtime.remoting>
 8     <application>
 9       <service>
10         <wellknown  mode="Singleton"
11                     type="RemotingObject.MyRemotingObject,RemotingObject"
12                     objectUri="MyRemotingObject"/>
13       </service>
14       <channels>
15         <channel port="9001" ref="tcp"/>
16         <channel port="9002" ref="http"/>
17         <channel portName="IpcTest" ref="ipc"/> <!--Ipc通道不需要端口号-->
18       </channels>
19     </application>
20   </system.runtime.remoting>
21 </configuration>

  此时,客户端程序的实现代码如下所示:

 1 using RemotingObject;
 2 using System;
 3 using System.Runtime.Remoting;
 4
 5 namespace RemotingClientByConfig
 6 {
 7     class Program
 8     {
 9         static void Main(string[] args)
10         {
11             //使用HTTP通道得到远程对象
12             RemotingConfiguration.Configure("RemotingClientByConfig.exe.config", false);
13             MyRemotingObject proxyobj1 = new MyRemotingObject();
14             if (proxyobj1 == null)
15             {
16                 Console.WriteLine("连接服务器失败");
17             }
18
19             Console.WriteLine("This call object by TcpChannel, 100 + 200 = {0}", proxyobj1.AddForTcpTest(100, 200));
20             Console.WriteLine("This call object by HttpChannel, 100 - 200 = {0}", proxyobj1.MinusForHttpTest(100, 200));
21             Console.WriteLine("This call object by IpcChannel, 100 * 200 = {0}", proxyobj1.MultipleForIPCTest(100, 200));
22             Console.WriteLine("Press any key to exit!");
23             Console.ReadLine();
24         }
25     }
26 }

  客户端配置文件为:

 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <configuration>
 3     <startup>
 4         <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
 5     </startup>
 6   <system.runtime.remoting>
 7     <application>
 8       <client>
 9         <wellknown  type="RemotingObject.MyRemotingObject,RemotingObject"
10                     url="http://localhost:9002/MyRemotingObject" />
11       </client>
12       <channels>
13         <channel ref="tcp" port="0"></channel>
14         <channel ref="http" port="0"></channel>
15         <channel ref="ipc" port="0"></channel>
16       </channels>
17     </application>
18   </system.runtime.remoting>
19 </configuration>

  使用配置文件修改后的分布式程序的运行结果与前面的运行结果一样,这里就不一一贴图了。

五、总结

  到这里,.NET Remoting技术的分享就结束了,本文只是对.NET Remoting技术做了一个基本的介绍,如果想深入了解.NET Remoting技术的话,推荐大家可以看看下面的专题细细品味C#——.Net Remoting专题。在下一篇文章中,继续为大家分享另一种分布式技术——Web Service。

   本文的示例代码文件下载:.NETRemotingSample

时间: 2024-08-24 20:04:14

跟我一起学WCF(2)——利用.NET Remoting技术开发分布式应用的相关文章

跟我一起学WCF(3)——利用Web Services开发分布式应用

一.引言 在前面文章中分别介绍了MSMQ和.NET Remoting技术,今天继续分享.NET 平台下另一种分布式技术——Web Services 二.Web Services 详细介绍 2.1 Web Services 概述 Web Services是支持客户端与服务器通过网络互操作的一种软件系统,是一组可以通过网络调用的应用程序API.在Web Services中主要到SOAP/UDDI/WSDL这三个核心概念,下面分别介绍下这三个概念的定义. SOAP:SOAP(Simple Object

跟我一起学WCF(13)——WCF系列总结

引言 WCF是微软为了实现SOA的框架,它是对微乳之前多种分布式技术的继承和扩展,这些技术包括Enterprise Service..NET Remoting.XML Web Service.MSMQ等.WCF推出的原因在于:微软想将不同的分布式技术整合起来,提供一个统一的编程模型,这样对于开发者来说绝对是好事.在过去的2个月时间内,我陆续写了WCF系列文章,这些文章只是自己这段时间学习WCF内容的一个学习过程和笔记,希望通过这种写博文的方式记录下来和总结.本系列并没有对WCF机制做一个深入解析

跟我一起学WCF(11)——WCF中队列服务详解

一.引言 在前面的WCF服务中,它都要求服务与客户端两端都必须启动并且运行,从而实现彼此间的交互.然而,还有相当多的情况希望一个面向服务的应用中拥有离线交互的能力.WCF通过服务队列的方法来支持客户端和服务之间的离线工作,客户端将消息发送到一个队列中,再由服务对它们进行处理.下面让我们具体看看WCF中的队列服务. 二.WCF队列服务的优势 在介绍WCF队列服务之前,首先需要了解微软消息队列(MSMQ).MSMQ是在多个不同应用之间实现相互通信的一种异步传输模式,相互通信的应用可以分布在同一台机器

[老老实实学WCF] 第五篇 再探通信--ClientBase

原文:[老老实实学WCF] 第五篇 再探通信--ClientBase 老老实实学WCF 第五篇 再探通信--ClientBase 在上一篇中,我们抛开了服务引用和元数据交换,在客户端中手动添加了元数据代码,并利用通道工厂ChannelFactory<>类创建了通道,实现了和服务端的通信.然而,与服务端通信的编程模型不只一种,今天我们来学习利用另外一个服务类ClientBase<>来完成同样的工作,了解了这个类的使用方法,我们对服务引用中的关键部分就能够理解了. ClientBase

[老老实实学WCF] 第八篇 实例化

老老实实学WCF 第八篇 实例化 通过上一篇的学习,我们简单地了解了会话,我们知道服务端和客户端之间可以建立会话连接,也可以建立非会话连接,通信的绑定和服务协定的ServiceContract 的SessionMode属性共同决定了连接是否是会话的.会话连接在会话保持阶段服务端可以记住客户端,而非会话连接则不会,相同客户端的多次调用会被认为是不同的客户端发起的. 会话这个特性是许多其他特性的基础,例如我们今天要学习的实例化.连接是否是会话对实例化的过程将产生不同的影响.今天我们就来研究这个问题.

[老老实实学WCF] 第十篇 消息通信模式(下) 双工

原文:[老老实实学WCF] 第十篇 消息通信模式(下) 双工 老老实实学WCF 第十篇 消息通信模式(下) 双工 在前一篇的学习中,我们了解了单向和请求/应答这两种消息通信模式.我们知道可以通过配置操作协定的IsOneWay属性来改变模式.在这一篇中我们来研究双工这种消息通信模式. 在一定程度上说,双工模式并不是与前面两种模式相提并论的模式,双工模式的配置方法同前两者不同,而且双工模式也是基于前面两种模式之上的. 在双工模式下,服务端和客户端都可以独立地调用对方,谁都不用等待谁的答复,同样也不期

[老老实实学WCF] 第三篇 在IIS中寄存服务

原文:[老老实实学WCF] 第三篇 在IIS中寄存服务 老老实实学WCF 第三篇 在IIS中寄宿服务 通过前两篇的学习,我们了解了如何搭建一个最简单的WCF通信模型,包括定义和实现服务协定.配置服务.寄宿服务.通过添加服务引用的方式配置客户端并访问服务.我们对WCF的编程生命周期有了一个最基本的了解. 在前两篇中演示的例子,一定要力求背着做下来,包括源程序.配置文件都要背着一行行的手写下来,这样才能有深刻的体会.WCF的知识零散复杂,必须扎扎实实的学习和练习.如果你还没有做到了然于胸,现在赶紧翻

[老老实实学WCF] 第六篇 元数据交换

原文:[老老实实学WCF] 第六篇 元数据交换 老老实实学WCF 第六篇 元数据交换 通过前两篇的学习,我们了解了WCF通信的一些基本原理,我们知道,WCF服务端和客户端通过共享元数据(包括服务协定.服务器终结点信息)在两个终结点上建立通道从而进行通信.我们通过手写代码(或配置)的方式为服务端编写了元数据信息,没有借助元数据交换就实现了通信.然而在实际应用中,元数据往往是很多的,而且重复编写元数据的工作也是不值得的,因此必然会用到元数据交换的方式让客户端获取元数据,本篇我们就来进一步了解一下元数

[老老实实学WCF] 第四篇 初探通信--ChannelFactory

原文:[老老实实学WCF] 第四篇 初探通信--ChannelFactory 老老实实学WCF 第四篇 初探通信--ChannelFactory 通过前几篇的学习,我们简单了解了WCF的服务端-客户端模型,可以建立一个简单的WCF通信程序,并且可以把我们的服务寄宿在IIS中了.我们不禁感叹WCF模型的简单,寥寥数行代码和配置,就可以把通信建立起来.然而,仔细品味一下,这里面仍有许多疑点:服务器是如何建起服务的?我们在客户端调用一个操作后发生了什么?元数据到底是什么东西?等等.我们现在对WCF的理