.net Remoting编程
.NET Remoting 提供了一种允许对象通过应用程序域与另一对象进行交互的框架。这也正是我们使用Remoting的原因。为什么呢?在Windows操作系统中,是将应用程序分离为单独的进程。这个进程形成了应用程序代码和数据周围的一道边界。如果不采用进程间通信IPC(Internet Process Connection)机制,则在一个进程中执行的代码就不能访问另一进程。这是一种操作系统对应用程序的保护机制。然而在某些情况下,我们需要跨过应用程序域,与另外的应用程序域进行通信,即穿越边界(参考MSDN)。
现在来介绍一下其中涉及到的几个比较重要的概念:
1.通道(channel)
在.Net Remoting中是通过通道(channel)来实现两个应用程序域之间对象的通信的。首先,客户端通过Remoting,访问通道以获得服务端对象,再通过代理解析为客户端对象。这就提供一种可能性,即以服务的方式来发布服务器对象。远程对象代码可以运行在服务器上(如服务器激活的对象和客户端激活的对象),然后客户端再通过Remoting连接服务器,获得该服务对象并通过序列化在客户端运行。
通道(channel)有4种严格说,很多资料上说有2-3种.应该有4种.HttpChanel和TcpChanel.
(1)HttpChannel。在将远程对象驻留在 ASP.NET 中时,可以使用此通道。此通道使用 HTTP 协议在客户端和服务器之间发送消息。可以使用HTTP协议中的加密机制.需要主机名字和端口号.
(2)TcpChannel。在将远程对象驻留在 操作系统服务或其他可执行文件中时,此通道使用 TCP 套接字在客户端和服务器之间发送消息。同样需要提供主机名字和端口号.不提供任何内置的安全功能。
(3)IPCChanel,进程间通道,只使用同一个系统内,进程之见的通信.不需要需要主机名字和端口号.
(4)自定义通道 。自定义的传输通道可以使用任何基本的传输协议UDP\SMTP\IPX\消息排队等机制进行通信.用户可以根据需要自定义方式协议,因此.Net Remoting相对其他机制更加的灵活。不提供任何内置的安全功能。
2.远程对象(Obeject)
在Remoting中要传递的对象,设计者除了需要了解通道的类型和端口号之外,无需再了解数据包的格式。但必须注意的是,客户端在获取服务器端对象时,并不是获得实际的服务端对象,而是通过代理获得它的引用。远程对象要继承自MarshalByRefObject类,这个可以使远程对象在remoting应用通信中使用,支持对象的跨域边界访问.
3.激活方式
(1)服务器端激活,又叫做知名WellKnown方式,是因为服务器应用程序在激活对象实例之前会在一个众所周知的统一资源标识符(URI)上来发布这个类型。然后该服务器进程会为此类型配置一个WellKnown对象,并根据指定的端口或地址来发布对象。服务器端激活又分为SingleTon模式和SingleCall模式两种。SingleTon模式:此为有状态模式。如果设置为SingleTon激活方式,则Remoting将为所有客户端建立同一个对象实例。SingleCall模式:SingleCall是一种无状态模式。一旦设置为SingleCall模式,则当客户端调用远程对象的方法时, Remoting会为每一个客户端建立一个远程对象实例,至于对象实例的销毁则是由GC自动管理的。
(2)客户端激活。与WellKnown模式不同, Remoting在激活每个对象实例的时候,会给每个客户端激活的类型指派一个URI。客户端激活模式一旦获得客户端的请求,将为每一个客户端都建立一个实例引用。SingleCall模式和客户端激活模式是有区别的:首先,对象实例创建的时间不一样。客户端激活方式是客户一旦发出调用的请求,就实例化;而SingleCall则是要等到调用对象方法时再创建。其次,SingleCall模式激活的对象是无状态的,对象生命期的管理是由GC管理的,而客户端激活的对象则有状态,其生命周期可自定义。其三,两种激活模式在服务器端和客户端实现的方法不一样。尤其是在客户端,SingleCall模式是由 GetObject()来激活,它调用对象默认的构造函数。而客户端激活模式,则通过CreateInstance()来激活,它可以传递参数,所以可以调用自定义的构造函数来创建实例。(详细参考MSDN)
(3)代理Proxy,客户端访问的不能直接访问远程对象,它是通过代理来访问代理上的方法.代理对象又分为透明代理和真实代理,区别是,在透明代理上,客户通过Invoke调用的是远程对象上真实代理的方法.然后把消息再传递给通道.
好了,介绍到此我们也基本了解.Net Remoting相关的知识,下面我们来学习的是具体的编程实现部分.程序大体分为3个部分远程对象\服务器\可户端.现在我们来分别实现.服务器端要添加引用System.Runtime.Remoting的程序集.
1.远程对象(RemoteOject),也就是我们远程要访问的对象.首先定义一个Class,继承MarshalByRefObject,可以使用在remoting应用中,支持对象的跨域边界访问.具体代码如下:
1namespace RemoteObject 2{ 3 //创建远程对象.继承MarshalByRefObject,可以使用在remoting应用中,支持对象的跨域边界访问 4 public class MyRemoteObject : MarshalByRefObject//访问远程对象需要通过代理 5 { 6 //简单的例子,实现加法功能的方法AddForTcpTest,这个远程对象可以实现封装业务逻辑或者数据访问等操作。 7 // 8 public int AddForTcpTest(int a, int b) 9 { 10 return a + b; 11 } 12 //实现减法功能的方法MinusForHttpTest,测试HTTP通道。 13 public int MinusForHttpTest(int a, int b) 14 { 15 return a - b; 16 } 17 //实现乘法功能的方法MultipleForIpcTest,测试Ipc通道 18 public int MultipleForIpcTest(int a, int b) 19 { 20 return a * b; 21 } 22 } 23 24}
对象分别定义了3种方法,目的是为了测试3种不同的通道方式的效果. //简单的例子,实现加法功能的方法AddForTcpTest,这个远程对象可以实现封装业务逻辑或者数据访问等操作。实现减法功能的方法MinusForHttpTest,测试HTTP通道。实现乘法功能的方法MultipleForIpcTest,测试Ipc通道.
2.服务器端,注册通道,以便进程间通信,我们这里注册了三种通道,分别是HttpChanel\TcpChanel\IPCChanel.具体代码如下:
1namespace NetRemotingHost 2{ 3 class NetRemotingHost 4 { 5 //创建宿主应用程序 6 static void Main(string[] args) 7 { 8 9 /**///////////////////////////////////////创建三种通道///////////////////////////////////////////////// 10 ///创建Tcp通道,使用端口10001 11 TcpChannel chanTcp = new TcpChannel(10001); 12 /**////创建Http通道,使用端口10002 13 HttpChannel chanHttp = new HttpChannel(10002); 14 /**////创建IPC通道,使用端口10003,IPC只适合同系统内进程的通信,所以不需要设置端口和主机名 15 IpcChannel chanIPC = new IpcChannel("FrankTestIpc"); 16 /**///////////////////////////////////////注册通道////////////////////////////////////////////////// 17 18 ///注册TCP通道 19 ChannelServices.RegisterChannel(chanTcp); 20 /**////注册HTTP通道 21 ChannelServices.RegisterChannel(chanHttp); 22 /**////注册IPC通道 23 ChannelServices.RegisterChannel(chanIPC); 24 25 /**/////////////////////////////////////////打印通道///////////////////////////////////////////////// 26 // 打印TCP通道的名称. 27 Console.WriteLine("The name of the TCPChannel is {0}.", 28 chanTcp.ChannelName); 29 // 打印TCP通道的优先级. 30 Console.WriteLine("The priority of the TCPChannel is {0}.", 31 chanTcp.ChannelPriority); 32 33 // 打印Http通道的名称. 34 Console.WriteLine("The name of the HttpChannel is {0}.", 35 chanTcp.ChannelName); 36 // 打印Http通道的优先级. 37 Console.WriteLine("The priority of the HttpChannel is {0}.", 38 chanTcp.ChannelPriority); 39 40 // 打印IPC通道的名称. 41 Console.WriteLine("The name of the IpcChannel is {0}.", 42 chanTcp.ChannelName); 43 // 打印IPC通道的优先级. 44 Console.WriteLine("The priority of the IpcChannel is {0}.", 45 chanTcp.ChannelPriority); 46 /**////////////////////////////////////////////注册对象///////////////////////////////////////////////// 47 //注册对象MyRemoteObject到Net Remoting运行库 48 //配置远程通信基础框架.第2个参数是对象的URI,“RemoteObject.MyRemoteObject”,客户端URL一定要和这个匹配,不然会出现查找不到代理对象的异常 49 RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemoteObject.MyRemoteObject), "RemoteObject.MyRemoteObject", WellKnownObjectMode.Singleton); 50 //远程对象激活方式是单件激活模式,每次调用共享一个对象,SingleCall激活模式会在每次调用的时候产生一个新对象 51 52 //向信道暴露一个IPC远程对象. 53 //RemotingConfiguration.RegisterWellKnownService(Type(typeof(RemoteObject), "RemoteObject.rem", System.Runtime.Remoting.WellKnownObjectMode.Singleton); 54 55 /**////////////////////For Debug///////////////////////////////////////////////////////////////////////// 56 Console.WriteLine("Press any key to exit!"); 57 System.Console.ReadLine(); 58 59 } 60 61 62 } 63}
创建Tcp通道,使用端口10001, 创建Http通道,使用端口10002,创建IPC通道,使用端口10003,IPC只适合同系统内进程的通信,所以不需要设置端口和主机名.然后调用ChannelServices类的静态方法RegisterChannel进行注册.最后一步注册对象MyRemoteObject到Net Remoting运行库,同时要先配置远程通信基础框架.第2个参数是对象的URI,“RemoteObject.MyRemoteObject”,客户端URL一定要和这个匹配,不然会出现查找不到代理对象的异常.WellKnownObjectMode.Singleton);远程对象激活方式是单件激活模式,每次调用共享一个对象,SingleCall激活模式会在每次调用的时候产生一个新对象.
3.客户端是控制台程序(实际项目类型可以替换,这里只是为了作为例子选择控制台类型).配置文件进行的设置如下:
1<configuration> 2 <appsettings> 3 <add key="ServiceURLTcp" value="tcp://localhost:10001/RemoteObject.MyRemoteObject"/> 4 <add key="ServiceURLHttp" value="http://localhost:10002/RemoteObject.MyRemoteObject"/> 5 <add key="ServiceURLIpc" value="ipc://FrankTestIpc/RemoteObject.MyRemoteObject"/> 6 </appsettings> 7 <system .runtime.remoting> 8 9 </system> 10</configuration>
配置文件设置的是具体通道的URL信息.具体c#实现代码如下:
1 2namespace RemoteClient 3{ 4 class MyClient//客户端 5 { 6 [STAThread]//主线程,建立客户端程序:注册通道,根据URL得到对象代理,使用代理调用远程对象 7 8 static void Main(string[] args) 9 { 10 //为远程对象创建代理 11 //代理的优势在于不仅可以跨域访问对象还可以跨进程,和系统,使用TCP通道 12 try 13 { 14 /**///// 15 RemoteObject.MyRemoteObject proxyObjectTcp = (RemoteObject.MyRemoteObject)Activator.GetObject(typeof(RemoteObject.MyRemoteObject), System.Configuration.ConfigurationSettings.AppSettings["ServiceURLTcp"]); 16 //通过代理访问对象的方法,输出结果 17 Console.WriteLine("This call object by TcpChannel,100+200 = {0}", proxyObjectTcp.AddForTcpTest(100, 200)); 18 19 /**/////// 20 RemoteObject.MyRemoteObject proxyObjectHttp = (RemoteObject.MyRemoteObject)Activator.GetObject(typeof(RemoteObject.MyRemoteObject), System.Configuration.ConfigurationSettings.AppSettings["ServiceURLHttp"]); 21 //通过代理访问对象的方法,输出结果 22 Console.WriteLine("This call object by HttpChannel,100-200 = {0}", proxyObjectHttp.MinusForHttpTest(100, 200)); 23 24 /**///// 注册一个远程对象的客户端代理. 25 //System.Runtime.Remoting.WellKnownClientTypeEntry remoteType = new System.Runtime.Remoting.WellKnownClientTypeEntry(typeof(RemoteObject.MyRemoteObject), "ipc://FrankTestIpc/RemoteObject.MyRemoteObject"); 26 //System.Runtime.Remoting.RemotingConfiguration.RegisterWellKnownClientType(remoteType);//如果需要客户端和住进程通讯,要在客户端注册代理,方式和服务器端注册注册远程对象的代理相同 27 28 RemoteObject.MyRemoteObject proxyObjectIpc = (RemoteObject.MyRemoteObject)Activator.GetObject(typeof(RemoteObject.MyRemoteObject), System.Configuration.ConfigurationSettings.AppSettings["ServiceURLIpc"]); 29 //通过代理访问对象的方法,输出结果 30 Console.WriteLine("This call object by IpcChannel,100*200 = {0}", proxyObjectIpc.MultipleForIpcTest(100, 200)); 31 } 32 catch (Exception e) 33 { 34 throw e; 35 } 36 finally 37 { 38 39 } 40 //For Debug 41 Console.WriteLine("Press any key to exit!"); 42 Console.ReadLine(); 43 } 44 45 } 46}
主进程通过配置获取远程对象的信息,为远程对象创建代理,代理的优势在于不仅可以跨域访问对象还可以跨进程,和系统,使用TCP通道,降低系统耦合性.最后客户端通过代理访问远程对象的方法.
运行要首先要运行服务器端,其次是客户端
浙公网安备 33010602011771号