按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
1。 设置注册表
在这儿,大家可看到对静态方法Naming。bind() 的一个调用。然而,这个调用要求注册表作为计算机上的一
个独立进程运行。注册表服务器的名字是 rmiregistry。在32 位Windows 环境中,可使用:
start rmiregistry
令其在后台运行。在Unix 中,使用:
rmiregistry &
和许多网络程序一样,rmiregistry 位于机器启动它所在的某个 IP 地址处,但它也必须监视一个端口。如果
象上面那样调用rmiregistry,不使用参数,注册表的端口就会默认为 1099。若希望它位于其他某个端口,
只需在命令行添加一个参数,指定那个端口编号即可。对这个例子来说,端口将位于 2005,所以
rmiregistry 应该象下面这样启动(对于32 位Windows):
start rmiregistry 2005
对于Unix ,则使用下述命令:
rmiregistry 2005 &
与端口有关的信息必须传送给bind()命令,同时传送的还有注册表所在的那台机器的 IP 地址。但假若我们
想在本地测试RMI 程序,就象本章的网络程序一直测试的那样,这样做就会带来问题。在 JDK 1。1。1 版本
中,存在着下述两方面的问题(注释⑦):
(1) localhost不能随 RMI 工作。所以为了在单独一台机器上完成对 RMI 的测试,必须提供机器的名字。为
了在32 位 Windows 环境中调查自己机器的名字,可进入控制面板,选择“网络”,选择“标识”卡片,其中
列出了计算机的名字。就我自己的情况来说,我的机器叫作“Colossus ”(因为我用几个大容量的硬盘保存
各种不同的开发系统——Clossus 是“巨人”的意思)。似乎大写形式会被忽略。
(2) 除非计算机有一个活动的TCP/IP 连接,否则 RMI 不能工作,即使所有组件都只需要在本地机器里互相通
信。这意味着在试图运行程序之前,必须连接到自己的 ISP (因特网服务提供者),否则会得到一些含义模
糊的违例消息。
⑦:为找出这些信息,我不知损伤了多少个脑细胞。
考虑到这些因素,bind()命令变成了下面这个样子:
Naming。bind(〃//colossus:2005/PerfectTime〃; pt);
若使用默认端口 1099,就没有必要指定一个端口,所以可以使用:
Naming。bind(〃//colossus/PerfectTime〃; pt);
在JDK 未来的版本中(1。1之后),一旦改正了 localhost 的问题,就能正常地进行本地测试,去掉 IP地
584
…………………………………………………………Page 586……………………………………………………………
址,只使用标识符:
Naming。bind(〃PerfectTime〃; pt);
服务名是任意的;它在这里正好为 PerfectTime,和类名一样,但你可以根据情况任意修改。最重要的是确
保它在注册表里是个独一无二的名字,以便客户正常地获取远程对象。若这个名字已在注册表里了,就会得
到一个AlreadyBoundException 违例。为防止这个问题,可考虑坚持使用 rebind() ,放弃bind()。这是由于
rebind()要么会添加一个新条目,要么将同名的条目替换掉。
尽管main()退出,我们的对象已经创建并注册,所以会由注册表一直保持活动状态,等候客户到达并发出对
它的请求。只要rmiregistry 处于运行状态,而且我们没有为名字调用 Naming。unbind()方法,对象就肯定
位于那个地方。考虑到这个原因,在我们设计自己的代码时,需要先关闭 rmiregistry,并在编译远程对象
的一个新版本时重新启动它。
并不一定要将rmiregistry 作为一个外部进程启动。若事前知道自己的是要求用以注册表的唯一一个应用,
就可在程序内部启动它,使用下述代码:
LocateRegistry。createRegistry(2005);
和前面一样,2005 代表我们在这个例子里选用的端口号。这等价于在命令行执行 rmiregistry 2005。但在设
计RMI 代码时,这种做法往往显得更加方便,因为它取消了启动和中止注册表所需的额外步骤。一旦执行完
这个代码,就可象以前一样使用Naming 进行“绑定”——bind()。
15。8。3 创建根与干
若编译和运行PerfectTime。java,即使rmiregistry 正确运行,它也无法工作。这是由于RMI 的框架尚未就
位。首先必须创建根和干,以便提供网络连接操作,并使我们将远程对象伪装成自己机器内的某个本地对
象。
所有这些幕后的工作都是相当复杂的。我们从远程对象传入、传出的任何对象都必须“implement
Serializable”(如果想传递远程引用,而非整个对象,对象的参数就可以“implement Remote”)。因此
可以想象,当根和干通过网络“汇集”所有参数并返回结果的时候,会自动进行序列化以及数据的重新装
配。幸运的是,我们根本没必要了解这些方面的任何细节,但根和干却是必须创建的。一个简单的过程如
下:在编译好的代码中调用rmic,它会创建必需的一些文件。所以唯一要做的事情就是为编译过程新添一个
步骤。
然而,rmic 工具与特定的包和类路径有很大的关联。PerfectTime。java 位于包 c15。Ptime 中,即使我们调用
与PerfectTime。class 同一目录内的rmic,rmic 都无法找到文件。这是由于它搜索的是类路径。因此,我们
必须同时指定类路径,就象下面这样:
rmic c15。PTime。PerfectTime
执行这个命令时,并不一定非要在包含了 PerfectTime。class 的目录中,但结果会置于当前目录。
若rmic 成功运行,目录里就会多出两个新类:
PerfectTime_Stub。class
PerfectTime_Skel。class
它们分别对应根(Stub )和干(Skeleton )。现在,我们已准备好让服务器与客户互相沟通了。
15。8。4 使用远程对象
RMI 全部的宗旨就是尽可能简化远程对象的使用。我们在客户程序中要做的唯一一件额外的事情就是查找并
从服务器取回远程接口。自此以后,剩下的事情就是普通的Java 编程:将消息发给对象。下面是使用
PerfectTime 的程序:
//: DisplayPerfectTime。java
// Uses remote object PerfectTime
package c15。ptime;
import java。rmi。*;
import java。rmi。registry。*;
public class DisplayPerfectTime {
public static void main(String'' args) {
System。setSecurityManager(
585
…………………………………………………………Page 587……………………………………………………………
new RMISecur ityManager());
try {
PerfectTimeI t =
(PerfectTimeI)Naming。lookup(
〃//colossus:2005/PerfectTime〃);
for(int i = 0; i 《 10; i++)
System。out。println(〃Perfect time = 〃 +
t。getPerfectTime());
} catch(Exception e) {
e。printStackTrace();
}
}
} ///:~
ID字串与那个用 Naming 注册对象的那个字串是相同的,第一部分指出了 URL 和端口号。由于我们准备使用
一个URL,所以也可以指定因特网上的一台机器。
从Naming。lookup()返回的必须造型到远程接口,而不是到类。若换用类,会得到一个违例提示。
在下述方法调用中:
t。getPerfectTime( )
我们可看到一旦获得远程对象的句柄,用它进行的编程与用本地对象的编程是非常相似(仅有一个区别:远
程方法会“掷”出一个 RemoteException 违例)。
15。8。5 RMI 的替选方案
RMI 只是一种创建特殊对象的方式,它创建的对象可通过网络发布。它最大的优点就是提供了一种“