- 写在前面的话
- 引言
- 第 1 章 对象入门
- 第 2 章 一切都是对象
- 第 3 章 控制程序流程
- 第 4 章 初始化和清除
- 第 5 章 隐藏实施过程
- 第 6 章 类再生
- 第 7 章 多形性
- 第 8 章 对象的容纳
- 第 9 章 违例差错控制
- 第 10 章 Java IO 系统
- 第 11 章 运行期类型鉴定
- 第 12 章 传递和返回对象
- 第 十三 章 创建窗口和程序片
- 第 14 章 多线程
- 第 15 章 网络编程
- 第 16 章 设计范式
- 第 17 章 项目
- 附录 A 使用非 JAVA 代码
- 附录 B 对比 C++和 Java
- 附录 C Java 编程规则
- 附录 D 性能
- 附录 E 关于垃圾收集的一些话
- 附录 F 推荐读物
15.8.2 远程接口的实施
服务器必须包含一个扩展了 UnicastRemoteObject 的类,并实现远程接口。这个类也可以含有附加的方法,但客户只能使用远程接口中的方法。这是显然的,因为客户得到的只是指向接口的一个句柄,而非实现它的那个类。
必须为远程对象明确定义构建器,即使只准备定义一个默认构建器,用它调用基础类构建器。必须把它明确地编写出来,因为它必须“掷”出 RemoteException 违例。
下面列出远程接口 PerfectTime 的实施过程:
//: PerfectTime.java // The implementation of the PerfectTime // remote object package c15.ptime; import java.rmi.*; import java.rmi.server.*; import java.rmi.registry.*; import java.net.*; public class PerfectTime extends UnicastRemoteObject implements PerfectTimeI { // Implementation of the interface: public long getPerfectTime() throws RemoteException { return System.currentTimeMillis(); } // Must implement constructor to throw // RemoteException: public PerfectTime() throws RemoteException { // super(); // Called automatically } // Registration for RMI serving: public static void main(String[] args) { System.setSecurityManager( new RMISecurityManager()); try { PerfectTime pt = new PerfectTime(); Naming.bind( "//colossus:2005/PerfectTime", pt); System.out.println("Ready to do time"); } catch(Exception e) { e.printStackTrace(); } } } ///:~
在这里,main() 控制着设置服务器的全部细节。保存 RMI 对象时,必须在程序的某个地方采取下述操作:
(1) 创建和安装一个安全管理器,令其支持 RMI。作为 Java 发行包的一部分,适用于 RMI 唯一一个是 RMISecurityManager。
(2) 创建远程对象的一个或多个实例。在这里,大家可看到创建的是 PerfectTime 对象。
(3) 向 RMI 远程对象注册表注册至少一个远程对象。一个远程对象拥有的方法可生成指向其他远程对象的句柄。这样一来,客户只需到注册表里访问一次,得到第一个远程对象即可。
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 地址,只使用标识符:
Naming.bind("PerfectTime", pt);
服务名是任意的;它在这里正好为 PerfectTime,和类名一样,但你可以根据情况任意修改。最重要的是确保它在注册表里是个独一无二的名字,以便客户正常地获取远程对象。若这个名字已在注册表里了,就会得到一个 AlreadyBoundException 违例。为防止这个问题,可考虑坚持使用 rebind(),放弃 bind()。这是由于 rebind() 要么会添加一个新条目,要么将同名的条目替换掉。
尽管 main() 退出,我们的对象已经创建并注册,所以会由注册表一直保持活动状态,等候客户到达并发出对它的请求。只要 rmiregistry 处于运行状态,而且我们没有为名字调用 Naming.unbind() 方法,对象就肯定位于那个地方。考虑到这个原因,在我们设计自己的代码时,需要先关闭 rmiregistry,并在编译远程对象的一个新版本时重新启动它。
并不一定要将 rmiregistry 作为一个外部进程启动。若事前知道自己的是要求用以注册表的唯一一个应用,就可在程序内部启动它,使用下述代码:
LocateRegistry.createRegistry(2005);
和前面一样,2005 代表我们在这个例子里选用的端口号。这等价于在命令行执行 rmiregistry 2005。但在设计 RMI 代码时,这种做法往往显得更加方便,因为它取消了启动和中止注册表所需的额外步骤。一旦执行完这个代码,就可象以前一样使用 Naming 进行“绑定”——bind()。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论