2013-03-26 134 views
3

我坚持至少5小时,现在没有其他度假村,但要问在这里。我正在编写一个RMI应用程序。我希望服务器绑定一个远程对象(这里是NoteBoardImpl),这将由客户端查找。客户端将侦听器(此处为NoteBoardListener)传递给服务器,侦听器也是客户端导出的远程对象。“java.rmi.ConnectException:连接拒绝主机”当导出客户端对象

我在这里准备了一个简单的SSCCE,所以我真的希望有人可以研究它。所有类都在默认包中的相同文件夹中。我知道这是令人沮丧的,我应该将应用程序拆分为三个罐子,但我希望在这里尽可能简单。

远程接口:

import java.rmi.Remote; 
import java.rmi.RemoteException; 

public interface INoteBoard extends Remote { 
    public void test(INoteBoardListener listener) throws RemoteException; 
} 


import java.rmi.Remote; 
import java.rmi.RemoteException; 

public interface INoteBoardListener extends Remote { 
    public void onNewText(String text) throws RemoteException; 
} 

接口的实现:

import java.rmi.RemoteException; 

public class NoteBoardImpl implements INoteBoard { 
    @Override 
    public void test(INoteBoardListener listener) throws RemoteException { 
     listener.onNewText("server call the listener"); 
    } 
} 


import java.rmi.RemoteException; 

public class NoteBoardListener implements INoteBoardListener { 
    @Override 
    public void onNewText(String text) throws RemoteException { 
     System.out.println(text); 
    } 
} 

客户端和服务器:

import java.rmi.Naming; 
import java.rmi.server.UnicastRemoteObject; 

public class Client { 
    public static void main(String[] args) { 
     if (args.length < 2) { 
      System.out.println("2 arguments required:\nRMI_IP RMI_port"); 
      return; 
     } 
     System.setProperty("java.rmi.server.hostname", args[0]); 
     try { 
      INoteBoard nb = (INoteBoard) Naming.lookup(String.format("rmi://%s:%s/note", args[0], args[1])); 
      INoteBoardListener l = (INoteBoardListener) UnicastRemoteObject.exportObject(new NoteBoardListener(), 0); 
      nb.test(l); 
      l.onNewText("client invokes listener"); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 
} 


import java.rmi.Naming; 
import java.rmi.server.UnicastRemoteObject; 

public class Server { 
    public static void main(String[] args) { 
     if (args.length < 2) { 
      System.out.println("2 arguments required:\nRMI_IP RMI_port"); 
      return; 
     } 
     System.setProperty("java.rmi.server.hostname", args[0]); 
     try { 
      INoteBoard noteBoard = (INoteBoard) UnicastRemoteObject.exportObject(new NoteBoardImpl(), 0); 
      Naming.rebind(String.format("rmi://%s:%s/note", args[0], args[1]), noteBoard); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 
} 

我试着模拟用于测试的分布式系统和运行在客户端的虚拟机上。主机-VM网络具有以下规格 - 主机IP = 192.168.56.1,VM IP = 192.168.56.101。
首先,我使用以下命令(预先启动了rmiregistry 1099)在本地运行客户端和服务器。工作目录是项目的根和编译的类在bin目录:
java -cp bin -Djava.rmi.server.codebase=http://student.agh.edu.pl/~grajewsk/bin/ Server 192.168.56.1 1099
java -cp bin Client 192.168.56.1 1099
和它的工作。

然后我就跑使用相同的命令虚拟机上的客户端程序,这里是我得到了异常:

java.rmi.ConnectException: Connection refused to host: 192.168.56.1; nested exception is: 
    java.net.ConnectException: Connection refused 
    at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:619) 
    at sun.rmi.transport.tcp.TCPChannel.createConnection(TCPChannel.java:216) 
    at sun.rmi.transport.tcp.TCPChannel.newConnection(TCPChannel.java:202) 
    at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:128) 
    at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:194) 
    at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:148) 
    at sun.proxy.$Proxy0.test(Unknown Source) 
    at Client.main(Client.java:14) 
Caused by: java.net.ConnectException: Connection refused 
    at java.net.PlainSocketImpl.socketConnect(Native Method) 
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:327) 
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:193) 
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:180) 
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:384) 
    at java.net.Socket.connect(Socket.java:546) 
    at java.net.Socket.connect(Socket.java:495) 
    at java.net.Socket.<init>(Socket.java:392) 
    at java.net.Socket.<init>(Socket.java:206) 
    at sun.rmi.transport.proxy.RMIDirectSocketFactory.createSocket(RMIDirectSocketFactory.java:40) 
    at sun.rmi.transport.proxy.RMIMasterSocketFactory.createSocket(RMIMasterSocketFactory.java:146) 
    at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:613) 
    ... 7 more 

注意对象如何成功查到的在服务器的注册表,然后客户端远程对象被导出(也是成功的),并且执行在第14行中断开,我尝试调用通过客户端对象的服务器端对象上的方法。

我在任何一个系统上都没有防火墙,ping在两个方向都完美无瑕。我知道这里肯定有一些概念上的问题,当然我误解了RMI的一些问题。我非常感谢你的帮助。

二进制代码库是on my student's server以及the source code。先谢谢你!

+0

可以ping 192.168 .56.1从192.168.56.101开始? – EJP 2013-03-27 05:41:59

+0

是的,正如我写的“双向ping完美无瑕”。另外,尽管我运行服务器的机器,我仍可以telnet到rmiregistry和服务器端口,所以我可以在主机或虚拟机上运行服务器,并且可以通过telnet连接到另一台机器的端口。 – Wojtek 2013-03-27 07:59:30

回答

3

好的,终于解决了。问题是,我在两个程序中传递了相同的java.rmi.server.hostname参数,这意味着客户端和服务器都在此处获得了服务器的地址。事实证明,如果客户想要导出自己的对象,它必须提供自己的IP给java.rmi.server.hostname。这样一切正常。

所以,概括起来讲,我不得不放弃3个参数传递给服务器:
RMI_IP RMI_PORT server_hostname

和3个类似的参数给客户端:
RMI_IP RMI_PORT客户端主机

4

下面是我要做的最底层的事情。

  1. 运行服务器应用
  2. 找出它使用什么端口RMI(它是短暂的,所以它应该为你创建服务器的每个进程改变)。

    • 找到PID用ps -ef
    • 然后-anp的netstat | grep按这应该然后给你的端口号。并且它应该绑定到0.0.0.0
  3. 在客户端虚拟机上,使用nc或telnet来验证您可以连接到端口。如果失败了,你可能会遇到防火墙/ iptables问题。

  4. 使用wireshark来验证您的客户端实际上是否尝试连接到您从第2步获悉的端口/ IP组合。

请记住,只是因为你可以ping通,并不意味着你可以连接到给定的端口。另外检查“iptables -L”。

另外,Naming表示不把URL的方案组件。格式应该是// host:port/name,所以你应该检查一下。

+0

谢谢你的建议,其实我并不知道测试端口连接的好方法。无论如何,无论我在主机上还是在虚拟机上运行服务器,一切似乎都可以正常工作。我知道我的一位朋友在虚拟机和主机之间运行时遇到问题,所以我明天会问他,希望能在这里发布解决方案。但是我仍然在等待你的建议,如果你能在这里发现一些明显的错误。 – Wojtek 2013-03-27 00:11:39

+0

如果你正在为你的虚拟机使用NAT连接,请尝试一个桥接连接..这可能只是你的问题,否则如果你想使用NAT,你将不得不设置一个端口转发(这可能不容易RMI)。虽然这并不能解释为什么你可以连接到客户端的RMIRegistry。 – user931366 2013-03-27 00:35:26

+0

我在VirtualBox中仅使用了一个仅适用于主机的适配器。我不太确定它是如何模拟网络的。稍后我会尝试桥接适配器。 – Wojtek 2013-03-27 07:57:18

相关问题