0

我在不同的机器上有客户端和服务器。

当我试图反序列化客户端上的对象时,我得到了这个异常。

java.rmi.UnmarshalException: error unmarshalling return; nested exception is: 
java.lang.ClassNotFoundException: task.MyTask

这是我的服务器代码:

System.setProperty("java.security.policy", "all_policy.policy");
System.setSecurityManager(new RMISecurityManager());
        try
        {
            mngr = new Manager();
            registry = LocateRegistry.createRegistry(15120);

            TaskDistributor stub = (TaskDistributor) UnicastRemoteObject.exportObject(mngr, 0);
            registry.rebind("Manager", stub);
        }
catch (Exception e)
        {
            System.out.println ("Ошибка при инициалиазации RMI: " + e.getMessage() + "\n");
            e.printStackTrace();
        }
...

try
    {
        //when creating a new task
        mngr.setTask(task);
    }
    catch (Exception e)
    {
        System.out.println ("Ошибка при передаче задания: " + e.getMessage() + "\n");
        e.printStackTrace();
    }

这是客户:

try
{
    //msg.getMessage() = MyTask.class.getClassLoader().getResource("").getPath() called on server machine
    System.setProperty("java.rmi.server.codebase", "file://" + server + msg.getMessage());
    Registry registry = LocateRegistry.getRegistry(server, 15120);
    TaskDistributor mngr = (TaskDistributor) registry.lookup("Manager");
    cg.append("Задание получено\n");

    MatrixMultiplier task = mngr.getTask(); //<------------Exception here
    task.multiply();

    mngr.setTask(task);

    client.sendMessage(new ChatMessage(ChatMessage.TASK_COMPLETED, ""));
}
catch (Exception e)
{
    System.out.println ("Ошибка: " + e.getMessage());
    e.printStackTrace();
}

如果我在同一台本地机器上使用它确实有效,所以我认为远程客户端 JVM 无法访问服务器类文件,或者我使用了错误的路径。

msg.getMessage() 返回:/C:/Projects/IDEA/KachNetworkLab/out/production/server/

任何建议将不胜感激。

解决方案:

这个 HttpServer 为我工作。也许其他人会发现这很有用...

HttpServer httpServer = HttpServer.create(address, 10);
httpServer.createContext("/", new HttpHandler() {
            @Override
            public void handle(HttpExchange t) throws IOException {
                try {
                File f = new File(t.getRequestURI().toString().substring(1).replace("\\", "/"));
                    System.out.println(t.getRequestURI().toString().substring(1).replace("\\", "/"));
                    if (!f.exists())
                    {
                        StringBuilder response = new StringBuilder().append("No luck :(");
                        t.sendResponseHeaders(HttpURLConnection.HTTP_OK, response.toString().getBytes().length);
                        t.getResponseBody().write(response.toString().getBytes());
                        t.getResponseBody().close();
                        t.close();
                        return;
                    }

                    t.sendResponseHeaders(HttpURLConnection.HTTP_OK, f.length());
                    InputStream file = new FileInputStream(f);
                    sendFile(file, t.getResponseBody());
                    t.getResponseBody().close();
                    t.close();
                }
                catch(Exception e)
                {
                    e.printStackTrace();
                }
            }
        });

        httpServer.setExecutor(null);
        httpServer.start();

调用System.setProperty("java.rmi.server.codebase", "http://" + IP + ":8000" + project root path on server);客户端将为 RMI 提供远程代码库路径。

例如,您可以通过调用 MyClass.class.getProtectionDomain().getCodeSource().getLocation().getPath()获取服务器上的根路径。

不过你应该小心。这样,任何人都可以访问您服务器计算机上的任何文件。明智的做法是在句柄中写入一些安全条件或修改策略文件。

4

1 回答 1

2

问题是在客户端上,您指定了一个file:URI,它告诉 JVM 在本地文件系统中查找类。当您在与服务器相同的实际机器上运行客户端 JVM 时,此方法有效,但如果您希望客户端能够从不同的机器下载类,则必须为其提供某种可以访问的 URI网络。您可以使用挂载的网络共享来执行此操作,但标准方法是通过 HTTP 使 jars(或裸类文件)可用。关于动态代码下载的Oracle 技术说明有详尽的解释和几个示例。

如果您希望 RMI 服务器能够“独立”运行而不与任何其他服务通信,您可以在其中嵌入一个绑定到随机端口的 Web 服务器,然后为客户端提供一个 RMI 方法来检索一个来自服务器的用于下载的 URI。

于 2013-11-10T06:12:29.997 回答