返回介绍

15.8.2 远程接口的实施

发布于 2024-10-15 23:56:34 字数 3571 浏览 0 评论 0 收藏 0

服务器必须包含一个扩展了 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文