粗 制 滥 造

引言

掌握远程过程调用原理,基于java RMI进行远程编程和控制。要求定义远程接口类及实现类:定义相应的处理方法;客户端利用RMI实现远程调用服务。同时,在在两台机器之间验证结果正确。

基于实验一的内容,基于RMI的远程调用,改写你的程序。

(2.2 实现A机器的程序,可以管理(增加、删除、改等)B机器上的某个文件夹或者目录)

项目仓库地址:https://github.com/unsioer/RmiFileManager

环境

开发环境:

  • JDK 11
  • IDEA 社区版(本地代码开发)
  • Visual Studio Code(远程SSH代码开发)
  • Apache Maven 3.6.3(包管理)
  • Swing (客户端GUI)
  • commons-ioFileUtils类含扩展的文件操作)
  • Git(代码管理)

详细设计

概要

本实验的服务器端和客户端均采用了Java RMI技术。

服务器端绑定注册RMI服务,实现对应的接口方法。客户端访问建立的URL,在指定的远程主机上查找RMI服务对象,找到后转换为本地接口,从而可以实现远程操作。

提交的实验代码中,服务器端有控制台版本,客户端则有控制台和GUI两种版本。

服务器端

跨平台远程连接的服务器端设置(重点)

本实验测试双机通信时,远程服务器系统为Ubuntu 20.04 LTS。由于ECS服务器存在内网和公网IP,服务器端应用就必须注意相应的处理,才能实现远程调用。

否则,就会出现如图所示的情况,出现远程内网IP:

解决方案的代码形如:

...
System.setProperty("java.rmi.server.hostname", hostname); //设置公网IP
...
try {
    RMISocketFactory.setSocketFactory(new MyRMISocket()); //定义内网数据传输端口

    Registry registry = LocateRegistry.createRegistry(port); //定义数据传输端口

    FileManager fileManager = new FileManagerImpl(); //

    registry.rebind("file", fileManager); //注册

    Context namingContext = new InitialContext();

    System.out.println(Inet4Address.getLocalHost().getHostAddress());
    namingContext.rebind("rmi://" + Inet4Address.getLocalHost().getHostAddress() + ":" + port + "/file", fileManager); //对象,把对象与一个名字绑定在内网IP上
} catch (IOException | NamingException e) { //捕获异常
    e.printStackTrace();
}

接口的实现

服务器端需要实现的RMI接口方法如下:

String hello() throws RemoteException; //返回"Hello World!",检查连接是否正常

/*
 * 获取指定路径文件(夹)本身的信息。
 * 如不存在,返回null
 * 参数:srcPath 文件路径
 */
File getFile(String srcPath) throws RemoteException; 

/*
 * 获取指定路径文件(夹)本身的信息。
 * 返回:FileInfoImpl文件信息自定义类,包含文件名、是否是文件夹等内容
 * 如不存在,返回null
 * 参数:srcPath 文件路径
 */
FileInfo getFileInfo(String srcPath) throws RemoteException; 

/*
 * 获取指定目录下的文件(夹)信息
 * 参数:srcDir 目录路径(若为空或”/"则返回根目录下的文件(夹)信息)
 */
ResponseImpl getFileList(String srcDir) throws RemoteException;

/*
 * 创建文件夹
 * 参数:srcDir 目录路径
 */
ResponseImpl createFolder(String srcDir) throws RemoteException;

/*
 * 移动文件(夹)到指定的目录下
 * 参数:srcPath 要移动的文件(夹)路径
 *      dstName 要移动到的目标目录路径
 */
ResponseImpl moveFile(String srcPath, String dstDir) throws RemoteException;  

/*
 * 复制文件(夹)到指定的目录下
 * 参数:srcPath 要复制的文件(夹)路径
 *      dstName 要粘贴到的目标目录路径
 */
ResponseImpl copyFile(String srcPath, String dstDir) throws RemoteException;

/*
 * 重命名文件(夹)
 * 参数:srcPath 要重命名的文件(夹)路径
 *      dstName 新的文件(夹)名称
 */
ResponseImpl renameFile(String srcPath, String dstName) throws RemoteException;

/*
 * 删除文件(夹)
 * 参数:srcPath 要删除的文件(夹)路径
 */
ResponseImpl deleteFile(String srcPath) throws RemoteException;

/*
 * 上传文件到指定的路径
 * 参数:info FileInfoImpl文件信息自定义类,包含文件名、要上传的路径、文件字节内容
 */
ResponseImpl uploadFile(FileInfoImpl info) throws RemoteException;

以上的ResponseImpl类为自定义Response接口的实现,主要含有成员:

public class ResponseImpl implements Response {
    Boolean status = false;//操作状态

    String message = null;//提示信息

    File[] files = null;//文件对象列表
    ...
}

以及对应的get set方法。

合法性检验

对于传入的字符串,如果是文件路径,不允许出现? * < > : ..(最后两个限制是防止操作根目录之外的文件夹)名。

如果是文件名,还不允许出现/\

客户端

客户端分为控制台版本和GUI版本。控制台部分,输入一行操作名称,再输入一至二行相应参数,最后执行操作。

控制台版本操作

  • ls:列目录
  • mkdir:新建文件夹
  • rm:删除
  • mv:移动
  • cp:复制
  • ren:重命名
  • upload:上传文件

GUI版本见下文。

GUI界面展示

GUI采用了swing。这里做了简要截图,也可详见演示视频。

基本界面

首先需要输入RMI服务的URL:

成功连接:

主界面

基本界面窗口显示标题和对应的RMI URL。

上方的工具条分别为:根目录按钮、上传按钮、输入框(输入并回车后直接访问指定目录)

主体部分是指定目录下的文件目录树。

右键菜单

右键菜单

上传

上传

新建文件夹

新建文件夹

重命名

重命名

剪切、复制、粘贴等不便在文档显示,详见视频。

技术总结

本次实验实现了一个最简单的基于Java RMI的远程文件管理应用,完成了实验所有要求并做了一些合法性检测,设计了相对简洁的GUI界面。

由于只是简易实现功能,尚有以下不足之处:

  • 请求和返回内容设计较为随意

  • 远程操作时,得到的文件对象许多属性丢失,导致对应的目录树均识别为文件,不利于查看(但仍可通过输入框访问指定的目录进行操作)

  • 未进行充分测试