通过 RMI 调用返回类实例
我试图通过 RMI 调用返回一个普通的类。我的服务器拥有一个名为 GameState 的类的实例,我想通过它的方法从客户端应用程序执行操作。因此,如果 a 只是返回一个 int 或其他东西,RMI 就可以正常工作,但是当尝试返回 GameState(它是 GameServer java 文件中定义的类)时,会发生以下错误(游戏状态未声明为 public、protected 或 private):
线程“main”中出现异常 java.lang.IllegalAccessError:尝试从类 $Proxy0 访问类 GameState 在 $Proxy0.getGameState(来源未知) 在 GameClient.login(GameClient.java:204) 在 GameClient.main(GameClient.java:168)
所以,我猜客户端应用程序知道 GameState 的外观,但没有任何访问权限?
我试图在自己的文件中将 GameState 设为公共类,但随后不同的连接客户端应用程序会获取各自的 GameState,因此似乎无法从服务器获取它。
以下是我认为相关的一些代码:
远程接口:
import java.rmi.*;
public interface ServerInterface extends Remote
{
public GameState getGameState() throws RemoteException;
}
一些如果服务器代码:
public class GameServer extends UnicastRemoteObject implements ServerInterface {
/**
*
*/
private static final long serialVersionUID = -6633456258968168102L;
private final static int DEFAULT_NAMING_PORT = 9955; // TODO: IMPORTANT - change this to a group-specific number,
// e.g., 2000 + group number. The number should be the same
// as in GameClient.java.
private final GameState m_state;
public static void main(String[] args) {
//the variables: port and host etc it configurated here, but has nothing to do with the RMI problem.
try {
GameServer instance = new GameServer(players);
System.out.print("Setting up registry on "+host+":"+port+" ... ");
//Set up an unrestricted security manager.
if (System.getSecurityManager() == null) {
// Set security manager to an instance of a dynamically created
// subclass of RMISecurityManager with the checkPermission() method overloaded
System.setSecurityManager(
new RMISecurityManager() {
@Override
public void checkPermission(Permission permission) {
}
}
);
}
// Create a registry for binding names (name server)
Registry naming = LocateRegistry.createRegistry(port);
System.out.println("done.");
String rmiObjectName = "GeschenktServer";
System.out.print("Binding name "+rmiObjectName+" ... ");
naming.rebind(rmiObjectName, instance);
System.out.println("done.");
} catch(RemoteException e) {
System.err.println("Could not start server: "+e);
System.exit(-1);
}
}
//the rest of the server code....
//the GameState declared in the same file
class GameState implements java.io.Serializable {
private static final long serialVersionUID = 545671487061859760L;
//the rest of the Game state code.
这是一些客户端代码:
private void login() {
try {
System.out.println("Connecting to server on host "+m_host+".");
// Set up an unrestricted security manager. In the server we trust.
// See GameServer.java for code explanation.
if (System.getSecurityManager() == null) {
System.setSecurityManager(
new RMISecurityManager() {
@Override
public void checkPermission(Permission permission) {
}
}
);
}
System.out.print("Locating registry on "+m_host+":"+m_port+" ... ");
Registry naming = LocateRegistry.getRegistry(m_host, m_port);
System.out.println("done.");
String name = "GeschenktServer";
System.out.print("Looking up name "+name+" ... ");
m_server = (ServerInterface) naming.lookup(name);
System.out.println("done.");
// TODO: Connect the player, i.e., register the player with the server.
// Make sure that the player cannot register if there are already enough players.
m_Id = m_server.getGameState().loginPlayer(m_name); //this line is causing the error...
if(m_Id < 0)
System.exit(0);
System.out.println("Server connection successful.");
m_window = new GameWindow(m_server, m_name, m_Id);
m_window.run();
} catch (Exception e) {
System.out.println("Connection failed - "+e);
System.exit(1);
}
}
}
我使用 eclipse 来完成这一切,并基于我对 eclipse 中的 RMI、rmic 的了解那些东西不再需要了,我是吗?
那么有人有什么想法吗?
提前致谢!
I'm trying to return a normal class via a RMI call. My server holds a instance of a class called GameState that i want to perform actions on via it's methods, from a client application. So the RMI works fine if a just return a int or something, but when a try to return GameState, which is a class defined inside the GameServer java file, the following error occurs (game state is declared neither public, protected or private):
Exception in thread "main" java.lang.IllegalAccessError: tried to access class GameState from class $Proxy0
at $Proxy0.getGameState(Unknown Source)
at GameClient.login(GameClient.java:204)
at GameClient.main(GameClient.java:168)
So, i guess the client application knows how GameState looks, but dont have any access to it?
I have tried to make GameState a public class in it's own file, but then the different connecting client applications get each their own GameState, so it's seems like that dont get it from the server.
Here are some code that i think is relevant:
The remote interface:
import java.rmi.*;
public interface ServerInterface extends Remote
{
public GameState getGameState() throws RemoteException;
}
Some if the server code:
public class GameServer extends UnicastRemoteObject implements ServerInterface {
/**
*
*/
private static final long serialVersionUID = -6633456258968168102L;
private final static int DEFAULT_NAMING_PORT = 9955; // TODO: IMPORTANT - change this to a group-specific number,
// e.g., 2000 + group number. The number should be the same
// as in GameClient.java.
private final GameState m_state;
public static void main(String[] args) {
//the variables: port and host etc it configurated here, but has nothing to do with the RMI problem.
try {
GameServer instance = new GameServer(players);
System.out.print("Setting up registry on "+host+":"+port+" ... ");
//Set up an unrestricted security manager.
if (System.getSecurityManager() == null) {
// Set security manager to an instance of a dynamically created
// subclass of RMISecurityManager with the checkPermission() method overloaded
System.setSecurityManager(
new RMISecurityManager() {
@Override
public void checkPermission(Permission permission) {
}
}
);
}
// Create a registry for binding names (name server)
Registry naming = LocateRegistry.createRegistry(port);
System.out.println("done.");
String rmiObjectName = "GeschenktServer";
System.out.print("Binding name "+rmiObjectName+" ... ");
naming.rebind(rmiObjectName, instance);
System.out.println("done.");
} catch(RemoteException e) {
System.err.println("Could not start server: "+e);
System.exit(-1);
}
}
//the rest of the server code....
//the GameState declared in the same file
class GameState implements java.io.Serializable {
private static final long serialVersionUID = 545671487061859760L;
//the rest of the Game state code.
Here is some of the client code:
private void login() {
try {
System.out.println("Connecting to server on host "+m_host+".");
// Set up an unrestricted security manager. In the server we trust.
// See GameServer.java for code explanation.
if (System.getSecurityManager() == null) {
System.setSecurityManager(
new RMISecurityManager() {
@Override
public void checkPermission(Permission permission) {
}
}
);
}
System.out.print("Locating registry on "+m_host+":"+m_port+" ... ");
Registry naming = LocateRegistry.getRegistry(m_host, m_port);
System.out.println("done.");
String name = "GeschenktServer";
System.out.print("Looking up name "+name+" ... ");
m_server = (ServerInterface) naming.lookup(name);
System.out.println("done.");
// TODO: Connect the player, i.e., register the player with the server.
// Make sure that the player cannot register if there are already enough players.
m_Id = m_server.getGameState().loginPlayer(m_name); //this line is causing the error...
if(m_Id < 0)
System.exit(0);
System.out.println("Server connection successful.");
m_window = new GameWindow(m_server, m_name, m_Id);
m_window.run();
} catch (Exception e) {
System.out.println("Connection failed - "+e);
System.exit(1);
}
}
}
I am using eclipse to do all this, and based on what i have red about RMI in eclipse, rmic and that stuff is not needed anymore, i'm i right?
So anyone with any idea?
Thanks in advance!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我不认为这是一个权限问题。我无法从上面的代码中确定,但我认为这是一个代码库问题。您是否也在客户端配置了代码库?
要反序列化
GameState
类,客户端需要能够加载类定义。该定义位于服务器实现中,而不是接口中。通常,服务器实现不应编译到客户端的类路径,只有接口应该编译到客户端的类路径。我不完全确定,因为在您的解决方案中,由于GameState
,接口似乎依赖于实现,顺便说一句,这不是一个好主意。不管怎样,尝试将代码库配置添加到您的 VM-args 中。假设您在本地主机上执行所有内容,它应该如下所示:其中
${workspace_loc}
是工作区的绝对路径,PROJECT-NAME
是服务器项目的名称。 Eclipse 将自动解析${workspace_loc}
,因此您只需设置您的PROJECT-NAME
作为旁注:如果您以这种方式实现它,则
GameState
对象传输到客户端并在客户端执行,对服务器的执行没有任何影响。这真的是你想要的吗?如果希望 GameState 实例在服务器端执行,GameState 还需要实现 Remote,而不是 Serialized >,并且在将其存根传输给客户端时需要将其导出。最后,正如您正确指出的那样,自 Java 1.5 以来您不需要使用 rmic
I don't think this is a permission problem. I cannot tell for sure from the code above, but I would assume it is a codebase problem. Did you configure the codebase also on client-side?
To deserialize the
GameState
class, the client needs to be able to load the class definition. This definition is located in the Server implementation and not the interface. Normally, the Server implementation should not be compiled to the client's classpath, only the interface should. I am not entirely sure, as in your solution the interface seems to have a dependency on the implementation due to theGameState
which is not a good idea btw. Anyways, try adding a codebase configuration to your VM-args. Assuming you execute everything on localhost, it should look like this:Where
${workspace_loc}
is the absolute path to your workspace andPROJECT-NAME
is the name of the server project. Eclipse will resolve${workspace_loc}
automatically, so you only need to set yourPROJECT-NAME
As a side note: If you implement it that way, the
GameState
object is transmitted to the client-side and is executed on the client, having no effect whatsoever on the execution of the server. Is this really what you want? If you want theGameState
instance to execute on the server-side,GameState
also needs to implementRemote
, notSerializable
, and you need to export it when transmitting its stub to the client.Finally, as you correctly stated, you do not need to use rmic since Java 1.5
这就是问题所在。只有GameServer类和同一个包中的类才能创建GameState的实例。您的 RMI 代理对象(存根)使其成为其自己文件中的公共类。
这是正确的。它被序列化到每个客户端。如果您想共享单个 GameState 并将其保留在服务器上,则它本身必须是导出的远程对象,并具有名为 GameState 的远程接口。
This is the problem. Only the GameServer class and classes in the same package can create instances of GameState. Your RMI proxy object (stub) Make it a public class in its own file.
That's correct. It is serialized to each client. If you want to share a single GameState and have it remain at the server, it has to be an exported remote object itself, with a remote interface called GameState.
IllegalAccessError
原因很简单:GameState is NOT public
但是,还有一个更大的问题:
您确实了解
loginPlayer
不会执行您希望执行的操作... GameState 是原始状态的副本。您希望 GameState 是Remote
不可序列化的,这样您就可以在服务器上执行操作,而不是每个客户端都获得无用的副本。The
IllegalAccessError
reason is simple:GameState is NOT public
However, there is a larger issue:
you do understand that
loginPlayer
will not do what you like it to... The GameState is a copy of the original state. You want GameState to beRemote
not serializable, so you can execute the operation on the server, not each client to get a useless copy of.