如何做网站搜索优化,建站公司山东济南兴田德润简介,网站制作 网站建设,西安网站建设专家Remoting 构架 1.客户端(客户应用程序) 客户端的处理包含三个基本的组成部分#xff0c;代理(Proxy)、格式器(Formatter) 和 通道(Channel)。 2.服务端(宿主应用程序) 服务端主要由 通道(Channel)、格式器(Formatter)、Stack Builder组成。 在服务端#xff0c;宿主程序保持着…Remoting 构架 1.客户端(客户应用程序) 客户端的处理包含三个基本的组成部分代理(Proxy)、格式器(Formatter) 和 通道(Channel)。 2.服务端(宿主应用程序) 服务端主要由 通道(Channel)、格式器(Formatter)、Stack Builder组成。 在服务端宿主程序保持着为Remoting所打开的端口的监听一旦通道收到消息它便将消息发送给FormatterFormatter将消息进行反序列化然后将消息发送给Stack BuilderStack Builder读取消息然后依据消息创建对象(可选)调用方法。方法返回时Stack Builder将返回值封装为消息然后再提交给FormatterFormatter进行格式化之后发送到通道传递消息。 3.Remoting对象的三种激活方式 上一章 .Net Remoting - Part.1 中我们提到了传值封送和传引用封送并各给出了一张示意图实际上传引用封送还分为了三种不同的方式下面来一一来介绍。对于传引用封送记住各种方式的共同点服务对象创建且一直保持在宿主程序中。 3.1客户激活(Client activated ) 对于每个客户创建了其专属的远程对象为其服务(由Part.1的代码可以看出对象的状态在两次方法调用中也是维持着的)。除此以外一个代理可以为多个客户对象服务。 客户激活模式的缺点就是 如果客户端过多时或者服务对象为“大对象”时服务器端的压力过大。另外客户程序可能只需要调用服务对象的一个方法但是却持有服务对象过长时间这样浪费了服务器的资源。 3.2 服务激活 Singleton(Server activated Singleton) 这个模式的最大特色就是所有的客户共享同一个对象。服务端只在对象第一次被调用时创建服务对象对于后继的访问使用同一个对象提供服务。 因为Sinlgton对象是在第一次访问(比如方法调用)时由.Net自动创建的后继的访问不能重新创建对象所以它不提供有参数的构造函数。另外由于Singleton对象的状态由其它对象所共享所以使用Singleton对象应该考虑线程同步 的问题。 3.3 服务激活 Single Call(Server activated Single Call) Single Call方式是对每一次请求(比如方法调用)创建一个对象而在每次方法返回之后销毁对象。由此可见Single Call 方式的最大特点就是 不保存状态。使用Single Call的好处就是不会过久地占用资源因为方法返回后对资源的占用就随对象被销毁而释放了。最后Single Call 方式也不允许使用由参数的构造函数。 Remoting程序的基本操作 1.服务程序集 我们首先创建服务程序集它即为向客户程序提供服务的远程对象的实现代码。先创建一个类库项目ServerAssembly然后创建类型ServerAssembly.DemoClass(为Part.1中的ClassLib.DemoClass添加了几个方法)。我们让它继承自MarshalByRefObject。 2.宿主应用程序 接下来我们新创建一个空解决方案ServerSide在其下添加一个新的控制台项目ServerConsole然后再将上面创建的项目ServerAssembly添加进来。除此以外还需要添加System.Runtime.Remoting的引用。 2.1 注册通道 实现宿主应用程序的第一步就是注册通道。通道是实现了System.Runtime.Remoting.Channels.IChannel的类。通道分为两种一种是发送请求的通道比如说客户应用程序使用的通道这种类型的通道还需要实现 System.Runtime.Remoting.Channels.IChannelSender 接口一种是接收请求的通道比如说宿主应用程序使用的通道这种类型的通道还需实现System.Runtime.Remoting.Channels.IChannelReceiver接口。 通常我们不需要实现自己的通道.Net 提供了三个内置的通道分别是 System.Runtime.Remoting.Channels.Http.HttpChannel、 System.Runtime.Remoting.Channels.Tcp.TcpChannel 以及 System.Runtime.Remoting.Channels.Ipc.IpcChannel。 由于 IpcChannel 不能跨机器(只能跨进程)所以我们仅使用最为常用的 HttpChannel和TcpChannel为例作为说明。它们均实现了 System.Runtime.Remoting.Channels 命名空间下的 IChannel、IChannelSender、IChannelReceiver接口所以它们既可以用于发送请求也可以用于接收请求。 接下来需要对通道进行注册然后对这个通道进行监听。对于同一个应用程序域同一类型(实际上是同一名称因为同一类型的通道默认名称相同)的通道只能注册一次。对同一机器来说同一端口也只能使用一次。同一应用程序域可以注册多个不同类型的通道。注册的方式是调用ChannelServices类型的静态方法RegisterChannel() 当通道从端口监听到新请求时它会从线程池中抓取一个线程执行请求从而可以不间断地对端口进行监听(不会因为处理请求而中断)。当关闭宿主程序时.Net会自动释放端口以便其他程序可以使用该端口。 2.2 注册对象 注册通道之后我们需要告诉.Net允许哪些类型可以被远程程序访问这一步骤称为注册对象。如同上面所说的有三种服务器端的远程对象类型客户激活对象、服务激活Single Call、服务激活Singleton。 客户激活对象的注册方式需要使用RemotingConfiguration类型的RegisterActivatedServiceType()静态方法 同一类型对象只可以用一种方式注册(客户激活 或者 服务激活)。即是说如果使用上面的方法注册对象那么要么调用 ClientActivated()要么调用ServerActivatedSingleCall()或者ServerActivatedSingleton()而不能都调用。上面的RegisterWellKnownServiceType()方法接受三个参数1.允许进行远程访问的对象类型信息2.远程对象的名称用于定位远程对象3.服务激活对象的方式Singleton或者Single Call。 2.3 对象位置 经过上面两步我们已经开启了通道并注册了对象(告诉了.Net哪个类型允许远程访问)。那么客户端如何知道远程对象位置呢如同Web页面有一个Url一样远程对象也有一个Url这个Url提供了远程对象的位置。客户程序通过这个Url来获得远程对象。 RemotingConfiguration类型还有一个ApplicationName静态属性当设置了这个属性之后对于客户激活对象可以提供此ApplicationName作为Url参数也可以不提供。如果提供ApplicationName则必须与服务端设置的ApplicationName相匹配对于服务激活对象访问时必须提供ApplicationName此时两种方式的Uri为下面的形式 protocal://hostadrress:port/ApplicationName/ObjectUrl // Server Activatedprotocal://hostadrress:port // Client Activated Objectprotocal:// hostadrress:port/ApplicationName // Client Activated Object 比如如果通道采用协议为tcp服务器地址为127.0.0.1端口号为8051ApplicationName设为DemoAppObjectUrl设为RemoteObject(ObjUrl为使用RegisterWellKnownServiceType()方法注册服务激活对象时第2个参数所提供的字符串注意客户激活对象不使用它)则客户端在访问时需要提供的地址为 tcp://127.0.0.1:8051/DemoApp/RemoteObject // Server Activated Objecttcp://127.0.0.1:8051/DemoApp // Client Activated Objecttcp://127.0.0.1:8051 // Client Activated Object 如果RemotingConfiguration类型没有设置ApplicationName静态属性则客户端在获取远程对象时不需要提供ApplicationName此时Url变为下面形式 protocal://hostadrress:port/ObjectUrl // Server Activated Objectprotocal://hostadrress:port // Client Activated Object 3.客户应用程序 我们现在再创建一个空解决方案ClientSide然后在其下添加一个控制台应用程序ClientConsole因为客户端需要ServerAssembly.DemoClass的元信息来创建代理所以我们仍要添加对ServerAssembly项目的引用。除此以外我们依然要添加 System.Runtime.Remoting程序集。 客户应用程序的任务只有一个获取远程对象调用远程对象服务。记得客户应用程序实际上获得的只是一个代理只是感觉上和远程对象一模一样。客户端获得对象有大致下面几种情况 3.1使用new操作符创建远程对象 客户应用程序可以直接使用new获得一个远程对象。例如下面语句 DemoClass obj new DemoClass(); 看到这里你可能很惊讶这样的话不是和通常的创建对象没有区别为什么创建的是远程对象(这里用“远程对象”只是为了说明方便要记得实际上是代理对象)而非本地对象呢(注意本地客户程序ClientConsole也引用了ServerAssembly项目)其实.Net和你一样它也不知道这里要创建的是远程对象所以在使用new创建远程对象之前我们首先要注册对象。注册对象的目的是告诉.Net这个类型的对象将在远程创建同时还要告诉.Net远程对象的位置。 我们知道远程对象有 客户激活 和 服务激活 两种可能因此客户程序注册也分为了两种情况 -- 注册客户激活对象注册服务激活对象。在客户端注册对象也是通过RemotingConfiguration类型来完成 // 注册客户激活对象private static void ClientActivated() { Type t typeof(DemoClass); // 下面两个 url 任选一个 string url tcp://127.0.0.1:8501; //string url tcp://127.0.0.1:8501/SimpleRemote; RemotingConfiguration.RegisterActivatedClientType(t, url);}// 注册服务激活对象private static void ServerActivated() { Type t typeof(DemoClass); string url tcp://127.0.0.1:8501/SimpleRemote/ServerActivated; RemotingConfiguration.RegisterWellKnownClientType(t, url);} 我们看到尽管在服务端服务激活有两种可能的方式Singleton和SingleCall但是在客户端服务激活的两种方式采用同一个方法RegisterWellKnownClientType()方法进行注册。所以我们可以说 服务端决定服务激活对象的运行方式(Singleton或SingleCall)。 3.2 其它创建远程对象的方法 当我们在客户端对远程对象进行注册之后可以直接使用new操作符创建对象。如果不进行注册来创建远程对象可以通过 RemotingServices.Connect()、Activator.GetObject()、Activator.CreateInstance()方法来完成 string url tcp://127.0.0.1:8501/SimpleRemote/ServerActivated;// 方式1DemoClass obj (DemoClass)RemotingServices.Connect(typeof(DemoClass), url);// 方式2DemoClass obj (DemoClass)Activator.GetObject(typeof(DemoClass), url);// 方式3object[] activationAtt { new UrlAttribute(url) };DemoClass obj (DemoClass)Activator.CreateInstance(typeof(DemoClass), null, activationAtt); 这几种方法RemotingServices.Connect()和Activator.GetObject()是最简单也较为常用的它们都是只能创建服务激活对象且创建的对象只能有无参数的构造函数并且在获得对象的同时创建代理。Activator.CreateInstance()提供了多达13个重载方法允许创建客户激活对象也允许使用有参数的构造函数创建对象并且可以先返回一个Wrapper(包装)状态的对象然后在以后需要的时候通过UnWrap()方法创建代理。CreateInstance()方法更详细的内容可以参考MSDN。 4.程序运行测试 Remoting 最让初学者感到困惑的一个方面就是 客户激活 与 服务激活 有什么不同什么时候应该使用那种方式。说明它们之间的不同的最好方式就是通过下面几个范例来说明现在我们来将上面的服务端方法、客户端方法分别进行一下组装然后进行一下测试(注意在运行客户端之前必须保证服务端已经运行) 4.1 客户激活方式 先看下客户激活方式服务端的Main()代码如下 static void Main(string[] args) { RegisterChannel(); // 注册通道 ClientActivated(); // 客户激活方式 Console.WriteLine(服务开启可按任意键退出... ); Console.ReadKey();} 客户端的Main()代码如下 static void Main(string[] args) { // 注册远程对象 ClientActivated(); // 客户激活方式 RunTest(Jimmy, Zhang); RunTest(Bruce, Wang); Console.WriteLine(客户端运行结束按任意键退出...); Console.ReadKey();}private static void RunTest(string firstName, string familyName) { DemoClass obj new DemoClass(); obj.ShowAppDomain(); obj.ShowCount(firstName); Console.WriteLine({0}, the count is {1}. ,firstName, obj.GetCount()); obj.ShowCount(familyName); Console.WriteLine({0}, the count is {1}.,familyName, obj.GetCount());} 程序运行的结果如下 程序运行的结果如下 其中第一幅图是服务端第二幅图是客户端我们起码可以得出下面几个结论 不管是对象的创建还是对象方法的执行都在服务端(远程)执行。服务端为每一个客户端(两次RunTest()调用各创建了一个对象)创建其专属的对象为这个客户提供服务并且保存状态(第二次调用ShowCount()的值基于第一次调用ShowCount()之后count的值)。可以从远程获取到方法执行的返回值。(客户端从GetCount()方法获得了返回值)上面的第3点看起来好像是理所当然的如果是调用本地对象的方法那么确实是显而易见的。但是对于远程来说就存在一个很大的问题远程对象如何知道是谁在调用它方法执行完毕将返回值发送给哪个客户呢此时可以回顾一下第一篇所提到的客户端在创建远程对象时已经将自己的位置通过消息发送给了远程。 最后我们再进行一个深入测试追踪对象是在调用new时创建还是在方法调用时创建。将RunTest()只保留一行代码 private static void RunTest(string firstName, string familyName) { DemoClass obj new DemoClass(); // 创建对象} 然后再次运行程序得到的输出分别如下 // 服务端方式: Client Activated Object服务端开启按任意键退出... DomoClass Constructor DomoClass Constructor // 客户端客户端运行结束按任意键退出... 由此可以得出结论使用客户激活方式时远程对象在调用new操作时创建。 4.2 服务激活方式 -- Singleton 我们再来看一下服务激活的Singleton方式。先看服务端代码(“按任意键退出”等提示语句均以省略下同) static void Main(string[] args) { RegisterChannel(); // 注册通道 ServerActivatedSingleton(); // Singleton方式} 再看下客户端的Main()方法 static void Main(string[] args) { // 注册远程对象 ServerActivated(); RunTest(Jimmy, Zhang); RunTest(Bruce, Wang);} 程序的运行结果如下 同上面一样第一幅为服务端第二幅图为客户端。从图中我们可以得出当使用Singleton模式时服务端在第一次请求时创建一个对象(构造函数只调用了一次)。对于后继的请求仅使用这个对象进行服务(即使再次调用构造函数也不会创建对象)同时多个客户端共享同一个对象的状态(ShowCount()的值累加)。 我们和上一小节一样再次将客户端的RunTest()只保留为“DemoClass obj new DemoClass(); ”一行语句然后运行程序得到的结果为 // 服务端方式: Server Activated Singleton服务端开启按任意键退出...// 客户端客户端运行结束按任意键退出... 这个结果出乎我们意料但它又向我们揭示了Singleton的另一个性质即使使用new操作符客户端也无法创建一个对象而只有在对象上第一次调用方法时才会创建。仔细考虑一下这个和上面的结论是类似的只是更深入了一步。 4.3 服务激活方式 -- SingleCall 最后我们看一下SingleCall方式注意到客户端的代码不需要做任何修改所以我们只需要切换一下服务端的激活方式就可以了 static void Main(string[] args) { RegisterChannel(); // 注册通道 ServerActivatedSingleCall();} 我们再次看一下运行结果 我们可能首先惊讶构造函数居然调用了有10次之多在每次RunTest()方法中各调用了5次。如同前面所说对于SingleCall方式来说对象对每一次方法调用提供服务换言之对于每一次方法调用创建一个全新的对象为其服务在方法执行完毕后销毁对象。我们再看下客户端的输出GetCount()方法全部返回0现在也很明确了因为每次方法调用都会创建新对象(在创建对象时int类型的count被赋默认值0)所以SingleCall方式是不会保存对象状态的。如果想要为对象保存状态那么需要另外的机制比如将状态存储到对象之外 public void ShowCount(string name, object clientId) { LoadStatus(this, clientId); // 加载对象状态 count; Console.WriteLine({0},the count is {1}., name, count); SaveStatus(this, clientId); // 存储对象状态} 其中LoadStatus()、SaveStatus()方法分别用于加载对象状态和 存储对象状态。注意到ShowCount()方法多了一个clientId参数这个参数用于标示客户程序的id因为服务端需要知道当前是为哪个客户程序加载状态。 最后我们再次进行一下上面两节将RunTest()只保留为创建对象的一行代码得到的运行结果和Singleton是一样的 // 服务端方式: Server Activated Singleton服务端开启按任意键退出...// 客户端客户端运行结束按任意键退出... 这说明使用SingleCall时即使使用了new 来创建对象也不会调用构造函数只有在调用方法时才会创建对象(调用了构造函数)。 Remoting中的传值封送 很多朋友可能此刻会感到些许困惑在Part.1的范例中我们讲述AppDomain时使用了传值封送和传引用封送两种方式但是上面的三种激活方式都属于传引用封送。那么如何进行对象的传值封送呢(将DemoClass直接传到本地)实际上在上面的例子中我们已经进行了传值封送这个过程发生在我们在客户端调用 GetCount() 时。为什么呢想一想count值本来是位于服务端的且int为可序列化对象(Serializable)在向客户端返回方法结果时count值被包装为了消息然后由服务端发送回了客户端最后在客户端进行了解包装及还原状态。 为了看得更清楚一些我们在ServerAssembly中再创建一个DemoCount类型然后对这个类型进行传值封送因为DemoCount仅仅是为了传送数据不包含任何行为所以我们将它声明为结构 public class DemoClass : MarshalByRefObject { // 其余方法略... // 示范传值封送 public DemoCount GetNewCount() { return new DemoCount(count); }}[Serializable]public struct DemoCount { private readonly int count; public DemoCount(int count) { this.count count; } public int Count { get { return count; } } public void ShowAppDomain() { AppDomain currentDomain AppDomain.CurrentDomain; Console.WriteLine(currentDomain.FriendlyName); }} 在DemoClass中我们又添加一个方法它根据count的值创建了DemoCount对象而DemoCount对象会通过传值封送传递到客户端。 现在修改客户端再重载一个RunTest()方法用来测试这次的传值封送 // 测试传值封送private static void RunTest() { DemoClass obj new DemoClass(); obj.ShowAppDomain(); // 显示远程对象所在应用程序域 obj.ShowCount(张子阳); // Count 1 DemoCount myCount obj.GetNewCount(); // 传值封送DemoCount myCount.ShowAppDomain(); // 显示DemoCount所在应用程序域 // 在客户端显示count值 Console.WriteLine(张子阳, count: {0}., myCount.Count);} 此时我们再次进行测试得到的结果如下 可以看到我们在客户端DemoCount上调用ShowAppDomain()方法时返回了ClientApp.exe可见DemoCount已经通过传值封送传递到了客户端。那么我们继续上面的问题如何将DemoClass整个传值封送过来呢首先我认为没有这个必要如果将服务对象整个封送到客户端来执行那么Remoting还有什么意义呢其次我们来看如何实现它。方法很简单我们创建一个工厂类作为远程服务对象然后将我们实际要传值封送到客户端的对象(比如DemoClass)作为工厂方法的返回值。这个例子我就不再演示了相信看过上面的示例您已经明白了。 转载于:https://www.cnblogs.com/MayGarden/archive/2010/06/21/1762195.html