2013-03-26 101 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

相關問題