深入研究VB.Net远程调用对象的机制 |
|||||||||||||||||||||||
第一节 引言 在.NET出现以前,COM和DCOM可以让你很容易地用运行在另一进程或者网络上另一台机器上的对象来进行交互作用。然而现在,.NET结构提供一种与之不同的机制--远程调用,用于在你的客户应用程序和远程对象之间的内部处理和LAN通讯。在本文中,我将介绍如何通过在Visual Basic.NET(VB.NET)中创建一个类库来使用远程对象,调用来自于另一进程或者网络上另一台机器的方法的客户端应用程序。如图 ![]() 你仍然可以在VB.NET中使用COM和DCOM,VB.NET支持一个交互机理,你可以用来在COM组件中调用来自.NET的代码。然而,这个机制是为了能够继承以前的系统支持而设计的而不是为了新技术开发的,所以你应该着眼于更多现在新的技术。 另外一个替代的机制就是使用Web服务调用远程对象,当你使用 Web服务时,你不能进行一个对象的相同的实例的一个以上的方法调用,一个新对象实例服务每次只能调用一个。使用Web服务还要求你在对象运行的机器上安装Internet Information Server ( IIS ),但是你需要的可能是一种不需要安装IIS的轻量级解决方案。 远程调用是最类似于DCOM的.NET中的技术。在Beta 1版中,.NET远程调用比 DCOM需要更多的准备工作,但是它是一种简单的后台技术,并且承诺当.NET正式版本发布的时候会更加容易。你可以在相同的机器上的一个单独的进程中使用远程调用激活对象,或通过网络激活运行在另外一台机器上的对象。使用远程调用,你可以进行对象的相同的实例上的更多的方法调用,或者你可以配置远程调用象 Web服务那样动作,并且为每一个方法调用创建新的对象实例。 为了使用远程调用工作,你需要安装.NET Framework SDK,并最好安装集成Visual Studio.NET(VS.NET)。让我们在单独的一台机器上开始测试,在一个来自客户端应用程序中的单独的进程中运行物体的远程调用过程,然后把这个对象的远程调用过程移到网络上的另外一台机器上。 第二节 了解简单的远程调用 虽然你可以使用远程调用来填补曾由DCOM来满足的市场,但是远程调用与DCOM不同,远程调用使用一个你选择的网络接口,而DCOM不同,它使用许多难以预测的网络接口来与你的对象通讯,这使得远程调用在大多数的环境中可以很容易的配置。 当你调用一个使用远程调用的对象时,你通过一个通道进行调用过程:这个远程调用通讯机制与在网络上的对象通讯。你可以选择两个不同的通道甚至还可以创建你自己的通道。.NET中提供的两个通道是HTTP通道和一个直接的 TCP socket通道,你使用这些通道中的一个(即使你可能在同一台机器上)激活一个对象。 如果你使用 HTTP通道,你就得使用HTTP协议和简单对象访问协议(SOAP)与远程调用对象通讯,即使你使用 HTTP,你也未必非得使用端口80,虽然这是Web服务器默认的端口,你可以指定任意端口号作为通信端口。 直接的 TCP socket通道也是类似的,如果你选择这个通道,所有的通信就使用一个专有协议和一个SOAP的二进制实现来完成。虽然说这有些不好的地方,但是它多多少少能够快一些,因为把数据编码成二进制格式的过程比使用SOAP效率更加高,因为它是基于XML的。 每次你的代码调用远程调用对象的一个方法,远程调用子系统把这个方法调用转换成一条并把它通过选定的通道发送到远程调用对象中去。远程调用子系统然后再把这个方法的返回结果转换成一条消息返回到客户端应用程序中。在 beta 1中,使用一个单独的" black box "远程调用组件处理这个过程,但是在以后将发布的.NET中,这个体系结构将更加开放,将提供更多的可延伸性和可控制性。 和 DCOM一样,远程调用对象必须在一个过程中运行。因为 Microsoft Transaction Server ( MTS )和 COM+都不是特意支持 .NET,所以你需要在服务器上实现你自己的主机应用程序来。 你的主机应用程序使用远程调用服务来在消息通道上监听。 其实这比听起来要容易,因为远程调用为你考虑到所有的细节。 然而,远程调用调试起来可能会比较困难。 稍后我将讨论一些可以使调试变得更容易的技术。 第三节 创建并使用一个类 在你能够通过网络调用一个对象之前,你必须有一个类库。所有的COM对象在你首次创建它们的过程中或者在机器中继续运行,在 VB.NET中,你可以使用<Serializable()>属性标记一个类,这样你的对象就是以字的方式发送,意思是说它是物理上被复制进入正在调用的应用程序过程中的。 显然,如果你想要激活一个在另外一个过程或另外一台机器中的对象的方法,你想要那个对象支持远程调用。要想这样的话,你可以从.NET系统类库中的MarshalByRefClass类或MarshalByRefComponent类中取得你的对象,与使用值传递的标准System.Object基本类不同,你是通过引用传递这些基本类。你也应该避免使用<Serializable>属性标记这个对象,因为你有可能并不希望这个对象连续,而是在服务器中运行它。 你可以创建一个简单的类库来检测外部远程调用,在 VS.NET集成环境中,创建一个新类库项目,取名为NETserver并把默认的 Class1.vb文件重命名为NETclass.vb。默认的情况下,这个类是从Component基本类中派生出来的,它是通过值传递的。你可以把 Inherits行改为通过引用传递你的类,把你的类改为MarshalByRefComponent的子类:
你可以添加方法到包含远程运行的代码的类上。例如,你可以编写一个方法从Pubs数据库中以一个作者ID来检索作者姓名,使用Imports语句添加一个引用到 System.Data.dll中。
然后,添加一个方法到你的类上,用来检索相应的数据行并把作者姓名作为结果返回。参见下面列出的程序代码:
你现在就有了一个可被远程调用的类了。 你的远程调用对象需要在它运行的地方有一个过程,并且这个主机过程需要使用远程系统来监听传到你的对象上的请求。 幸运的是,代码使得这过程很简单的。添加一个新的Console Application项目到 VS.NET中,在Solution Explorer中,添加新的项目。 你可能需要向下滚动卷轴找到Console Application图标, 改名为 NEThost。添加一个引用到System.Runtime.Remoting.dll并添加一个 Imports语句,然后使用远程系统 :
在控制台应用程序中,当应用程序启动时, Sub Main方法自动运行。 你可以在这个方法中添加几行代码来设置远程系统:
ConfigureRemoting方法接收文件名并把它当成一个参数。这个文本文件包含配置远程调用所必需的信息,你需要创建这个文件,我过一会将介绍如何创建。 第四节 保持这个应用程序运行 你还需要确定这个主应用程序直到你准备好时才退出,这个主应用程序一终止,你的远程对象对任何客户就变得不可用。在本例中,你保持这个应用程序运行直到用户按下回车键,但在一个实际的应用程序中,你应该选择为主应用程序生成一个 Windows 2000或 Windows NT服务应用程序,以便它始终能够运行。 首先,你需要通过点击 Project菜单引用NETserver项目,然后添加引用和项目标签(参见图 2),选择 NETServer并单击 OK,在模块 1的顶部添加一条导入语句: 导入 NETserver ![]() 这样你的类对这个主应用程序就可用了,对于那些能够创建这个对象的实例的远程系统就很关键。 主配置文件描述了你的对象以及如何到达。使用这个文件来设置主应用程序,以便它能够在适当的通道里监听适当的信息。在Solution Explorer中,右击你的主应用程序并从菜单中选择Add New Item(添加新的项目)。命名这个文件为 host.cfg。这个文件是一个简单的文本文件,使用井号(#)作为分隔符,并且它有三行代码能为你的远程对象服务。 第一行指出主应用程序的名称。名称可以是任何东西,但是客户机可以使用它来找到主机:
接下来的这行定义调用的对象,它描述了这个类的类型名称,这个类所处的集合,应该监听消息的主机的统一资源标识号(URI)以及对象运行的方式:
你需要在一个行中输入前面所有的代码,在项目之间不留空格。这是 beta 1中隐含的唯一的文件格式;将来,你将能使用 XML来格式化这个文件。 远程调用支持两种方式: Singleton和 SingleCall。 第三行配置当与对象通信时使用的通道。本例中使用的HTTP通道, 8085端口:
在一行中输入所有的代码,项目中间不留空格。 一旦你构建了解决方案,你就可以打开一个控制台窗口并运行主应用程序。以命令行形式进入 NEThost\u30446目录并执行 NEThost.exe。应用程序指出这个网络类已经被初始化了。它现在监听来自客户机的请求。 你还可以以编程方式配置主机,而不使用配置文件。配置文件很好用,因为你可以使用它来改变主应用程序使用的通道或端口而不必重编译和重新部署。然而,如果你喜欢的话,你也可以跳过配置文件而直接地从代码中配置远程服务。它的功能上与使用配置文件相当;你可以选择任一种方法。 修改Sub Main方法来配置主机:
首先,你要创建一个 HTTPChannel对象,初始化它来使用 8085端口,然后使用 ChannelServices注册这个通道。接下来,你可以注册远程对象,只要提供集合名称、完整的类名、客户机使用的 URI以及对象的存取方式。 当你使用 DCOM调用一个远程对象时,你的客户机代码与你的项目中直接调用的对象的代码不同。VB.NET并不保留这个特性,你的 VB.NET客户应用程序需要包含配置远程系统的代码以使远程对象可用的。不管对象是本地的还是远程的,调用这个对象上方法的实际代码都是相同的,但是你需要添加几行额外代码来配置这个远程系统。 在 VS.NET中创建一个新Windows应用程序项目并把它命名为 NETclient,添加一个NETserver集合的引用,并添加一个 Imports语句:
这个语句使你的客户应用程序可使用远程类,你现在可以添加一个按钮到窗体中,并写按钮的代码来调用对象:
最后,你需要配置远程服务。使用 NEThost应用程序中相同的代码来完成这个任务,添加一个 System.Runtime.Remoting.dll的引用并添加一句 Imports语句:
在窗体的New方法中添加一行代码来配置远程服务:
与主应用程序一样,你需要创建一个配置文件以便客户应用程序知道如何定位和联络主应用程序和你的远程对象。 这个配置文件也是一个简单的文本文件,就象主机配置文件。它也包含三行代码,使用井号(#)作为分隔符。第一行代码标识客户程序名称:
下面的这行代码标识你的远程对象集合:
在一行中输入所有的代码,项目中间不留空格。 你需要指定远程主应用程序的应用程序名和包含这个远程对象的集合的名称。然后提供这个远程类的完整类名和用户将访问的类的完整的URI。从你在主机配置文件中指定的 URI中得到这个 URI,并且指定通信协议(HTTP://)、主机(localhost)和端口(:8085)。 最后,你需要指出与主应用程序通信时使用的通道,这是你包含在主机配置文件中的相同的一行代码:
在一行中输入所有的代码,项目中间不留空格。你必须在客户机和主机之间使用公共的通道;否则,它们就不能够通信。 第五节 配置客户端 就象主应用程序一样,你可以选择以编程方式配置客户应用程序而不是使用一个配置文件。然而,在beta 1中,在客户端上的改动并不像在服务器端上的改动那么容易,因为你还需要改变创建每一个对象实例的方法的代码。为了不使用New关键字来创建对象,你还需要使用Activator.GetObject方法。这利用了一个附带在客户机上的配置文件。 改变配置客户机的方法,只要把调用替换为ConfigureRemoting,如下所示:
这两行代码配置 HTTP通道,以便为使用它做好准备。然后你必须搜寻客户应用程序,找到使用 New关键字创建远程对象的代码:
使用这个语句替换前面的那一行语句:
这行语句很复杂,它调用GetObject方法从指定URI取得一个 NETserver.NETclass类型的对象。然后使用CType函数把得到的结果对象强制转换成 NETclass类型以便你可以在你的代码中使用它。 你的客户机代码剩余部分就可以保持不变了,如果你决定使用编程配置你的客户应用程序,我推荐在一个函数中封装对象的创建过程,如下:
然后,当你想得到这个对象的一个引用的话,你可以使用这个函数:
虽然这还不象使用 New关键字那么简单,它还是封装代码来创建远程对象。 远程程序很难调试,这是因为你的客户应用程序有到你的远程程序直接的引用,当远程程序失败时而你的客户机看上去仍然在工作。这是因为如果远程调用过程失败的话,客户机就会试图调用对象的局部拷贝。 你可以证明在这个函数返回它的值之前,通过在NETclass中添加一行代码调用远程对象来打印一则信息到控制台窗口:
现在,在你的主应用程序运行的服务器控制台窗口中,当它被调用时这个功能显示一则信息。当你运行客户应用程序时转换到控制台窗口并保证它显示这则信息。事实上,这证明你正在与远程对象交互。 如果你在连接远程对象时遇到了麻烦,你可以通过使用浏览程序来测试这个主应用程序。在一个控制台窗口中运行这个主应用程序,然后打开 Internet Explorer并且定位到这个远程对象的 URI。在我的例子中,你将定位到这个 URI:
如果主机正确操作,你应该看作为显示为一个页面或 XML的远程对象的SOAP定义(参见图3)。如果你没有看,主机可能就没能正确配置。 ![]() 你可以使用远程调用来创建 N层应用程序,只要通过调用 Active Server Pages ( ASP.NET )或者来自 VB.NET客户端应用程序中的 remote组件。 你也可以通过把 NEThost放进 Windows 2000 Service Application中,来增强这个例子的功能。因为那样的话,服务器在这台机器运行的任何时候都可用。你还可以使用它从一个数据库中返回数据,或者执行其他需要的服务器端处理过程。 你还可以使用 ASP.NET页面或者 VB.NET Windows应用程序来替换本文中的简单的例子,这样你就可以向你的用户显示你丰富的才华了。 |