【问题标题】:Can't use RMI remote methods无法使用 RMI 远程方法
【发布时间】:2017-02-12 19:25:28
【问题描述】:

3 天后无法解决此问题。 RMI 在 1 或 2 台 PC 上与 localhost 或 192.168.* 一起正常工作,但远程客户端无法使用远程方法。 启动 rmiregisry,然后使用 Naming.rebind() 绑定服务

注册表创建成功,我可以获取绑定服务列表,但我什至无法使用任何方法。

使用

搜索远程服务
Naming.lookup("rmi://host_ip:1099/qwe). 
or
Registry reg = LocateRegistry.getRegistry("host_ip",server);
reg.lookup("qwe")

客户端找到对象但不能使用它的方法。

代码如下: 服务器.java:

public class Server extends UnicastRemoteObject{

protected Server() throws RemoteException {
}

public static void main(String[] args) throws InterruptedException, RemoteException {
    ServiceServerImpl server = new ServiceServerImpl();
    try {
        System.setProperty("java.rmi.server.hostname", "host_ip");
        Naming.rebind("/qwe", server);
        Arrays.asList(Naming.list("rmi://localhost")).forEach(System.out::println);
        System.out.println(System.getProperty("java.rmi.server.hostname"));
    } catch (Exception e) {
        e.printStackTrace();
    }
    server.startServer();
}
}

ServiceServer.java:

public interface ServiceServer extends Remote{
String[] getLoginsList() throws RemoteException;
void addLogin(String login) throws RemoteException;
void removeLogin(String login) throws RemoteException;
boolean canConnect(String login) throws RemoteException;
void setConnected(String login) throws RemoteException;
void setDisconnected(String login) throws RemoteException;
}

ServiceServerImpl.java(如果需要):

public class ServiceServerImpl extends UnicastRemoteObject implements ServiceServer {
private ServerSocket socketListener;
private Socket clientSock;
private volatile List<ClientHandler> threadsList = new ArrayList<ClientHandler>();
private volatile Map<String, Boolean> logins = new HashMap<String,Boolean>();
// private Timer timer;

public ServiceServerImpl() throws RemoteException {
}

public void startServer() {
    // startTimer();
    try {
        socketListener = new ServerSocket(5001);
        System.out.println("server up...");
        while (true) {
            clientSock = null;
            while (clientSock == null)
                clientSock = socketListener.accept();
            ClientHandler curClientHandler = new ClientHandler(clientSock, this);
            threadsList.add(curClientHandler);
            System.out.println("user connected: " + clientSock.getInetAddress());
        }
    } catch (Exception e) {
        System.err.println("Socket exception");
        e.printStackTrace();
    }
}

public synchronized void removeUserThread(ClientHandler handler) {
    try {
        threadsList.remove(handler);
    } catch (NullPointerException e) {
    }
    System.out.println("- 1");
}

@Override
public void setConnected(String login){
    logins.put(login,true);
}

@Override
public void setDisconnected(String login){
    logins.put(login,false);
}

@Override
public boolean canConnect(String login) throws RemoteException{
    return logins.keySet().contains(login)&& (!logins.get(login));
}

@Override
public String[] getLoginsList() throws RemoteException {
    return this.logins.keySet().toArray(new String[this.logins.size()]);
}

@Override
public void addLogin(String login) throws RemoteException {
    logins.put(login, false);
    System.out.println("added");
}

@Override
public void removeLogin(String login) throws RemoteException {
    logins.remove(login);
}


public Map<String, Boolean> getLogins() {
    return logins;
}

public Set<String> getAllowedLogins() {
    return logins.keySet();
}

public List<ClientHandler> getThreadsList() {
    return threadsList;
}
}

我用 cmd 或 eclipse 启动 rmiregistry,然后启动服务器。 它正确启动,我无法连接本地主机或 192.168.0。* 但它不适用于远程客户端 尝试使用 .policy 文件 - 仍然不起作用

我得到下一个错误代码:

Exception in thread "main" java.rmi.ConnectException: Connection refused to host: 192.168.0.83; nested exception is: 
java.net.ConnectException: Connection timed out: connect
at sun.rmi.transport.tcp.TCPEndpoint.newSocket(Unknown Source)
at sun.rmi.transport.tcp.TCPChannel.createConnection(Unknown Source)
at sun.rmi.transport.tcp.TCPChannel.newConnection(Unknown Source)
at sun.rmi.server.UnicastRef.invoke(Unknown Source)
at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(Unknown Source)
at java.rmi.server.RemoteObjectInvocationHandler.invoke(Unknown Source)
at com.sun.proxy.$Proxy0.canConnect(Unknown Source)
at server.Main.main(Main.java:16)
Caused by: java.net.ConnectException: Connection timed out: connect
at java.net.DualStackPlainSocketImpl.connect0(Native Method)
at java.net.DualStackPlainSocketImpl.socketConnect(Unknown Source)
at java.net.AbstractPlainSocketImpl.doConnect(Unknown Source)
at java.net.AbstractPlainSocketImpl.connectToAddress(Unknown Source)
at java.net.AbstractPlainSocketImpl.connect(Unknown Source)
at java.net.PlainSocketImpl.connect(Unknown Source)
at java.net.SocksSocketImpl.connect(Unknown Source)
at java.net.Socket.connect(Unknown Source)
at java.net.Socket.connect(Unknown Source)
at java.net.Socket.<init>(Unknown Source)
at java.net.Socket.<init>(Unknown Source)
at sun.rmi.transport.proxy.RMIDirectSocketFactory.createSocket(Unknown Source)
at sun.rmi.transport.proxy.RMIMasterSocketFactory.createSocket(Unknown Source)
... 8 more

请帮忙

【问题讨论】:

  • 可能您遇到了防火墙或其他设置等连接问题。尝试通过telnet从客户端连接到服务器:telnet server_ip server_port
  • Server 不应扩展 UnicastRemoteObjectServiceServerImpl 不应该创建套接字。
  • @EJP 已修复..仍然无法正常工作
  • 我没说会。它是作为评论发布的,而不是作为答案发布的。
  • 请停止从您的问题中删除代码。

标签: java rmi


【解决方案1】:

你需要:

  1. java.rmi.server.hostname设置为你系统的外部IP地址,也就是你路由器的公网IP地址。
  2. 通过super(port)将远程对象导出到固定端口号。
  3. 在您的路由器中为这些端口和端口 1099 安排端口转发。

如何做 (3) 取决于路由器并且离题。

【讨论】:

  • 如果我在接口 impl 中使用 super(port) 并且它不能使用它(运行时端口已在使用中)
  • 那么,错误,使用另一个端口?如果您的问题中缺少相关问题,您应该解决它。
  • 如果这一切真的“已经完成了,伙计”,你就不会遇到问题,也不会刚刚发现端口冲突。
  • 你应该没有问题了,因为你不应该再尝试连接到 192.168.*.
  • 好的我现在明白了。所以你应该在 interface_imlp 的构造函数中使用 super(port_name) 然后使用 THAT(port_name) 端口启动服务器?或者我应该使用 super(5005) 然后启动注册表使用 1099 默认端口?
【解决方案2】:

根据您的描述,您遇到了一般的网络连接问题,这不是 RMI 特有的。我的建议是搁置 RMI 并使用 netcat 或其他一些通用客户端/服务器实现,直到你整理好连接的东西,然后再回到 RMI。

我的猜测是,您必须至少跨越两层安全。一个是运行 RMI 服务器的 PC 阻止了所有端口上的连接,或者可能除了一小部分端口之外的所有端口。您首先需要在 RMI 主机上打开端口 1099(这是默认的 RMI 端口,当然您可以选择其他端口)。如果这样做,您应该能够从同一本地网络上的另一台主机连接到 RMI 主机(即在同一子网 192.168.something.something 上)。

如果您使该连接正常工作,则要跨越的下一个桥是本地网络上的路由器可能会阻止来自本地网络外部的流量。请记住,这是一项重要的安全措施。我的建议是避免从外部直接打开与 RMI 主机的连接,因为这存在安全风险。而是尝试建立一个 SSH 隧道,然后使用该隧道将连接从外部传送到 RMI 主机。 SSH 隧道并不太复杂;网络搜索应该会找到解释。

客户端也可能阻止到端口 1099 的出站连接;您必须启用这些连接,或使用 SSH 隧道来避免该问题。

在早期(大约 1999 年),我建立了一个通过 RMI 进行通信的全球网络(嗯,大约 4 个站点)。当时的网络安全没有障碍。所以当然可以通过 RMI 进行远程通信,你只需要通过防火墙。祝你好运,玩得开心。

【讨论】:

  • 连接正常。客户端找到远程对象但无法使用它的方法
  • @shaldnikita 可以连接到注册表。与远程对象的连接不正常。
  • @EJP 它连接到远程注册表但不能调用任何方法
  • @shaldnikita 好的,感谢您提供的信息。 RMI 使用随机分配的端口在客户端和远程对象之间进行通信。因此,即使您能够连接到端口 1099,您也可能无法连接到随机端口。这里有更多信息:stackoverflow.com/questions/3071376/…
  • @shaldnikita 如果您在 Linux 系统上工作,请尝试 netstat 以查看打开了哪些端口进行侦听。 (netstat 也可能在其他系统上可用。)
【解决方案3】:

192.168.X.X 是一个私有/C 类 IP 地址,用于在本地网络上识别您的机器。要将您的服务公开给其他人/互联网用户,您需要在路由器上配置端口转发。它还有助于使用免费的动态 DNS 服务,例如 NoIP,以确保您的计算机即使在重新启动后仍然可以访问(假设您的 ISP 几乎总是会为您分配一个新的动态公共 IP 地址)。

一旦解决了这个问题,请确保您没有将 RMI 服务器绑定到 localhost127.0.0.1,而是绑定到 0.0.0.0

【讨论】:

  • 如何保证?
  • @shaldnikita 你没有绑定到 127.0.0.1,你不能不使用RMIServerSocketFactory。这无关紧要。
  • @shaldnikita:个人防火墙因阻止所有传入的未知外部流量而臭名昭著(这是正确的)。你确定你的服务器机器有适当的规则吗?如果您使用的是防火墙+防病毒软件,请确保您允许 RMI 服务器端口上的所有传入流量。
  • @SanjayT.Sharma 我通过路由器和防火墙打开了所需的端口
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-01
  • 2016-10-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多