remoting 通道.doc_第1页
remoting 通道.doc_第2页
remoting 通道.doc_第3页
remoting 通道.doc_第4页
remoting 通道.doc_第5页
已阅读5页,还剩13页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

23.3.2 Remoting的信道前面提到过,Remoting有多种信道可以选择,这大大增加了我们分布式系统的灵活性。如果希望在广域网通信,可以使用HTTP信道,如果希望在局域网通信取得更好的性能,可以使用TCP信道,如果希望在本机上的不同进程间通信以获得最好的性能,可以使用IPC信道。下面我们来修改前面的程序,使之使用三种不同的Remoting信道,并且我们要比较三种信道在效率上差多少:1首先在远程对象中新增一个方法,使之返回大量的数据。using System;namespace RemoteObject public class MyObject : MarshalByRefObject public int Add(int a, int b) return a + b; public string GetData() string data = new string100000; for (int i = 0; i data.Length; i+) datai = 很大量的数据+i; return data; 2然后修改服务端,使之在三个不同的信道上发布远程对象。using System;using System.Runtime.Remoting;using System.Runtime.Remoting.Channels;using System.Runtime.Remoting.Channels.Tcp;using System.Runtime.Remoting.Channels.Http;using System.Runtime.Remoting.Channels.Ipc;namespace TestRemotingConsoleServer class Program static void Main(string args) / 新建一个TCP信道 TcpChannel tc = new TcpChannel(9999); / 新建一个HTTP信道 HttpChannel hc = new HttpChannel(8888); / 新建一个IPC信道 IpcChannel ic = new IpcChannel(testPipe); / 注册TCP信道 ChannelServices.RegisterChannel(tc, false); ChannelServices.RegisterChannel(hc, false); ChannelServices.RegisterChannel(ic, false); / 注册知名对象 RemotingConfiguration.RegisterWellKnownServiceType(typeof (RemoteObject.MyObject), myObject, WellKnownObjectMode.SingleCall); / 让控制台不会自动关闭 Console.ReadLine(); 注意,由于是在同一个机器上注册多个信道,需要给每个信道使用不同的端口。对于IPC信道来说,使用一个管道名来区分而不是端口号。为了能更好地测试三者的差别,我们把服务端部署到另外一个服务器上(把EXE文件和DLL文件复制过去)。3在RemotingTest.aspx页面上新建三个按钮用于使用不同的信道调用远程对象。按钮的单击事件处理方法如下:protected void btn_HttpChannel_Click(object sender, EventArgs e) System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); sw.Start(); RemoteObject.MyObject mo = (RemoteObject. MyObject)Activator.GetObject(typeof(RemoteObject.MyObject), http:/srv-devapphost:8888/myObject); Response.Write(HTTP信道); Response.Write(string.Format(记录数:0条, mo.GetData().Length); Response.Write(string.Format(花费时间:0毫秒, sw.ElapsedMilliseconds);protected void btn_TcpChannel_Click(object sender, EventArgs e) System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); sw.Start(); RemoteObject.MyObject mo = (RemoteObject.MyObject)Activator.GetObject (typeof(RemoteObject.MyObject), tcp:/srv-devapphost:9999/myObject); Response.Write(TCP信道); Response.Write(string.Format(记录数:0条, mo.GetData().Length); Response.Write(string.Format(花费时间:0毫秒, sw.ElapsedMilliseconds);protected void btn_IpcChannel_Click(object sender, EventArgs e) System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); sw.Start(); RemoteObject.MyObject mo = (RemoteObject. MyObject)Activator.GetObject(typeof(RemoteObject.MyObject), ipc:/testPipe/myObject); Response.Write(IPC信道); Response.Write(string.Format(记录数:0条, mo.GetData().Length); Response.Write(string.Format(花费时间:0毫秒, sw.ElapsedMilliseconds);可以看到,使用三种信道调用的代码仅在URL上有区别。在这里我们不但把服务端部署到了远程服务器上,而且在本地也开了一个服务端用于在IPC上注册远程对象。4测试结果如图23-22所示。图23-22 Remoting的三种信道可以看到在效率上三者有明显的差别。IPC比TCP快是因为它传递数据不经过网络,不占用网络资源。TCP比HTTP快很多是因为默认情况下TCP信道使用二进制序列化,序列化后的数据量很小,而HTTP默认使用SOAP消息进行格式化,基于XML的SOAP消息非常臃肿,因此在传输上会比TCP花费更多的时间。不过不可否认HTTP信道在跨防火墙上的优势,因此使用哪种信道还需要根据自己的需求来选择。23.3.3 使用配置文件增加灵活性虽然我们做的Remoting程序可以正常使用,但是整个程序非常不灵活: 服务端有关信道、端口等的配置都直接写死在程序里面。 客户端设置的远程对象的地址也是写死在程序里面的。对于客户端的配置不是大问题,因为其实那个URL就是一个字符串。而服务端的配置文件应该怎么做呢?其实一点也不复杂,添加一个app.config然后写入下面的内容: 可以看到配置文件主要由两部分构成: 定义远程对象类型的service节点。在这里我们定义了一个知名对象,模式是SingleCall,对象名为myObject。 定义信道的channels节点。在这里定义了三个信道,和先前程序方式定义的一样。特别需要注意的是,这里的type=RemoteObject.MyObject,TestRemoteObject,格式是:type=命名空间.类型名,程序集名对比图23-17看看,现在你知道为什么当时笔者要把命名空间、类型和程序集三者的名字设置不同了吧。那么,怎么让服务端加载配置文件读取Remoting的配置呢?只需要一行代码就行。RemotingConfiguration.Configure(TestRemotingConsoleServer.exe.config, false);Console.ReadLine();你可能会奇怪,配置文件是app.config,为什么这里写成了应用程序名.config呢?其实在编译的时候IDE会自动把配置文件进行改名,以免发生冲突,如图23-23所示,可以看到Release目录的 文件。图23-23 服务端程序release文件夹真正有用的是加亮的三个文件(分别是远程对象、服务端和配置文件),在部署的时候只需要复制这些文件即可。虽然改了服务端,但是我们并没有改变通道的端口,因此客户端不需要做任何修改就能直接运行。如果你希望把URL从程序中分离的话,可以在配置文件中添加几个节点。 然后在代码中调用配置文件读取URL。RemoteObject.MyObject mo = (RemoteObject.MyObject)Activator.GetObject(typeof(RemoteObject.MyObject),ConfigurationManager.AppSettingsHTTPChannel);其他两个信道的代码差不多,就不列出来了。现在这样就非常灵活了,修改信道、修改端口甚至转移服务端的位置只需要重新调整配置文件即可。23.3.4 使用接口降低耦合读者首先要明确一点,客户端调用的远程方法是在服务端执行的。如下,我们在远程对象中增加一个方法。public void HelloWorld() Console.WriteLine(编程快乐);重新编译服务端和客户端,运行客户端可以看到服务端控制台程序上输出了“编程快乐”字样,如图23-24所示。那么问题就来了,既然远程对象是在服务端执行的,客户端为什么要引用远程对象呢?假设我们的报表系统是使用.NET Remoting开发的,难道要把核心DLL也公布给客户吗(要知道.NET应用程序是很容易被反编译得到“源代码”的)?其实,客户端只需要得到远程对象的“描述”,知道远程对象的类型以及成员定义,让客户端代码能编译通过即可。具体方法是什么,怎么实现,客户端并不关心。那么,怎么构建这个供客户端使用的壳子呢?有两种方法。 直接使用工具比如soapsuds.exe来生成。 使用基于接口的编程方法。由于篇幅关系,在这里我们仅仅介绍第二种方法的实现:1新建一个类库项目ITestRemoteObject,这个类库是前面TestRemoteObject的接口(Interface),因此以字母I开头。2打开TestRemoteObject下的RemoteObject.cs,把鼠标放在MyObject类上单击右键,选择“重构”“提取接口”,如图23-25所示。单击“全选”按钮选中所有成员,单击“确定”按钮。可以看到TestRemoteObject类库下面多了一个cs文件,如图23-26所示。 图23-25 提取接口 图23-26 自动生成的接口IMyObject就是MyObject类对应的接口。打开这个文件可以看到接口其实就是对类成员的定义,没有实际的实现。using System;namespace TestRemoteObject interface IMyObject int Add(int a, int b); string GetData(); void HelloWorld(); 对这个接口我们要进行一些改动: 要让接口能被外部调用,需要把接口加上公有访问修饰符。 系统自动以程序集的名字作为命名空间的命名,我们还是改回原来的RemoteObject。using System;namespace RemoteObject public interface IMyObject int Add(int a, int b); string GetData(); void HelloWorld(); 3现在这个接口在远程对象文件中,我们需要把它移动到ITestRemoteObject中,直接点击文件,Ctrl+X(剪切)、CTRL+V(粘贴)即可。4回头看MyObject文件:public class MyObject : MarshalByRefObject, TestRemoteObject.IMyObject系统自动让它继承了TestRemoteObject.IMyObject,刚才我们把TestRemoteObject修改成了RemoteObject,现在这里也需要同样修改。既然让类实现接口,那么就需要让TestRemoteObject项目引用ITestRemoteObject项目。右键单击TestRemoteObject项目,选择添加引用,在项目选项卡中找到ITestRemoteObject项目,单击“确定”按钮即可。现在两个项目的结构应该如图23-27所示。你可能会问,接口仅仅是对类的一个定义吗?不仅仅是这样,接口还对类有约束力,如果你修改了接口也一定要修改“实现”。如果你在接口中新加入一个Test()的方法,而不修改“实现”,编译程序会得到编译错误,如图23-28所示。 图23-27 基于接口的编程 图23-28 类需要实现接口的成员5现在,我们的客户端就可以引用和使用接口,而不是直接引用和使用远程对象了。首先右键单击TestWeb网站,选择属性页。在引用页找到原来的远程对象TestRemoteObject,删除它的引用,并添加ITestRemoteObject的引用,如图23-29所示。图23-29 修改网站项目的引用查找替换Remoting.aspx.cs中的所有RemoteObject. MyObject为RemoteObject.IMyObject,比如:RemoteObject.IMyObject mo = (RemoteObject.IMyObject)Activator.GetObject(typeof(RemoteObject.IMyObject), ConfigurationManager.AppSettingsTCPChannel);mo.HelloWorld();6重新编译解决方案,先后运行服务端和客户端,效果和原来的没有什么不同。但是,这样的方式更灵活了,或者说耦合更低了。为什么这样说呢?因为,现在如果希望在服务端的实现中做什么改动的话,不需要重新编译和部署客户端程序。23.3.5 使用Windows服务承载远程对象现在的程序看似很完美,但是要想真正应用还有一些问题。我们的服务端是一个控制台应用程序,如果在服务器上需要有10个Remoting的服务端,那么我们服务器重启动后也需要重启动这10个程序吗?读者可能会说可以把它们加入开始菜单的启动中让程序自动启动。但是你有没有想过,在登录到服务器进行维护的时候很容易不小心把控制台程序关闭了,而且关闭之后还不知道。要想解决这个问题就需要使用一种后台式的程序来作为服务端,Windows服务正好可以满足这个要求,而且还可以设置Windows服务自动启动。使用VS 2005创建.NET的Windows服务非常简单,下面我们一起来实现Windows服务版本的Remoting服务端。1创建一个新的Windows服务项目TestRemotingService,如图23-30所示。图23-30 创建新的Windows服务项目2打开Service1代码视图,找到OnStart部分,加入代码。protected override void OnStart(string args)System.Runtime.Remoting.RemotingConfiguration.Configure(AppDomain.CurrentDomain.BaseDirectory + TestRemotingService.exe.config, false);这句代码实现在Windows服务启动的时候从Windows服务安装目录所在的配置文件加载Remoting配置,然后把先前控制台服务端的配置文件复制过来。现在这个Windows服务是Remoting的服务端,因此也别忘记添加对TestRemoteObject远程对象的引用。3切换到Service1的设计视图,在空白处右键单击,然后选择“添加安装程序”选项。如图23-31所示。图23-31 添加服务安装程序4打开系统自动生成的ProjectInstaller.cs,如图23-32所示,可以看到页面上有两个组件。图23-32 服务安装程序单击serviceProcessInstaller1组件,观察属性窗口,如图23-33所示。在这里我们把Account属性设置为LocalSystem,作为服务的账户类型。然后单击serviceInstaller1组件,观察属性窗口,如图23-34所示。 图23-33 ServiceProcessInstaller组件 图23-34 ServiceInstaller组件在这里可以设置服务友好名、服务的描述、服务名和启动方式。只需要把StartType设置为Automatic,服务就能在系统重新启动后自动启动。5现在就可以安装服务了,单击“开始”菜单“所有程序”Microsoft Visual Studio 2005Visual Studio Tools“Visual Studio 2005命令行提示”,如图23-35所示,使用installutil程序来安装Windows服务。图23-35 使用installutil工具安装Windows服务如果你觉得输入exe所在路径太麻烦,可以直接打开文件夹把exe文件拖入命令行窗口。卸载服务使用u参数。installutil -u Windows服务exe所在路径6执行“我的电脑右键”“管理”“服务和应用程序”“服务”命令。如图23-36所示,可以在列表中找到我们的服务。图23-36 服务已经安装成功查看这个服务的属性,如图23-37所示。图23-37 Windows服务的属性7如果程序写的没有什么问题的话(其实我们只写了一行代码),服务应该能正常启动,然后可以打开网站进行测试。注意:由于安全问题,必须为Windows服务指定一个有效账户(Account=User)才能使用IPC信道,在这里就不详细叙述了。除了使用Windows服务承载远程对象外,还可以使用IIS。不过需要注意,使用IIS承载远程对象只能在HTTP信道上通信,好处在于可以使用IIS来进行安全管理。需要说的是,HTTP方式的Remoting效率非常低(甚至不如Web Service),因此不推荐。具体实现IIS部署Remoting的方法在这里就不说明了。23.3.6 异步操作在介绍Web服务的时候,我们介绍了异步调用Web服务的操作,在这里我们将介绍如何异步调用远程对象的方法。1首先在远程对象中加一个耗时2秒的方法。public string LongWork() System.Threading.Thread.Sleep(2000); return 编程快乐;别忘记同时更新接口。using System;namespace RemoteObject public interface IMyObject int Add(int a, int b); string GetData(); void HelloWorld(); string LongWork()

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论