簡體   English   中英

我是否需要客戶端、服務器和注冊表上的所有類才能使 RMI 工作?

[英]Do I need all classes on the client, server and registry for RMI to work?

我正在使用 RMI 邁出第一步,我有一個簡單的問題。

我有一個 .jar 文件,它實現了庫中的幾種方法。 我想使用 RMI 在 .jar 文件中調用此方法。

我正在嘗試創建一種包裝器來做到這一點。

所以,我正在做這樣的事情:

接口 class :該接口具有由遠程 object 實現的方法。

實現class :這個class,有接口方法的實現,每個實現調用.jar文件中對應的方法。 例如,jar 文件有一個名為 getDetails() 的方法,它返回一個“ResponseDetail” object。 ResponseDetail 是我在.jar 中的響應 class。

服務器 class :它將方法綁定到 rmiregistry

客戶端 class :它將使用在 implementation 中實現的方法。

到目前為止,一切都很好? :)

現在,我有一個 lib 文件夾,其中包含 .jar 文件。

服務器機器中,我部署了接口、實現和服務器類。 我已經生成了存根,並且成功運行了 rmiregistry,但是,有以下詳細信息:

要啟動 rmiregistry,我必須在命令行中設置類路徑以引用 .jar 文件,否則我會得到 java.lang.NoClassDefFoundError。 我用 this.sh 文件做到了:

THE_CLASSPATH=
for i in `ls ./lib/*.jar`
do
    THE_CLASSPATH=${THE_CLASSPATH}:${i}
done

rmiregistry -J-classpath -J".:${THE_CLASSPATH}" 

要啟動服務器,我還必須設置類路徑以引用.jar 文件,否則,我會得到 java.lang.NoClassDefFoundError。 我用過這樣的東西:

THE_CLASSPATH=
for i in `ls ./lib/*.jar` do
  THE_CLASSPATH=${THE_CLASSPATH}:${i}
done

java -classpath ".:${THE_CLASSPATH}" Server

客戶端機器:要從客戶端機器運行 Client.class 文件,我必須將 .jar 文件復制到它,並在類路徑中引用它們,否則,它不會運行,我得到 Z93F725A074B33FE1C886ZlangD48886Zlang44。 NoClassDefFoundError。 我不得不在客戶端機器上使用它:

THE_CLASSPATH=
for i in `ls ./lib/*.jar`
do
   THE_CLASSPATH=${THE_CLASSPATH}:${i}
done

java -classpath ".:${THE_CLASSPATH}" HelloClient

這個可以嗎? 我的意思是,我是否必須將 .jar 文件復制到客戶端計算機才能通過 RMI 執行方法?

).在 JDK v5 之前,必須使用rmic)生成 RMI 存根。 這是從 JDK v5 開始自動完成的。 此外,您還可以從 Java 代碼中啟動 RMI 注冊表。 要從一個簡單的 RMI 應用程序開始,您可能需要執行以下步驟:

  1. 創建接口:
     import java.rmi.*; public interface SomeInterface extends Remote { public String someMethod1() throws RemoteException; public int someMethod2(float someParameter) throws RemoteException; public SomeStruct someStructTest(SomeStruct someStruct) throws RemoteException; }
  2. 實現接口:
     import java.rmi.*; import java.rmi.server.*; public class SomeImpl extends UnicastRemoteObject implements SomeInterface { public SomeImpl() throws RemoteException { super(); } public String someMethod1() throws RemoteException { return "Hello World;"; } public int someMethod2( float f ) throws RemoteException { return (int)f + 1. } public SomeStruct someStructTest(SomeStruct someStruct) throws RemoteException { int i = someStruct;getInt(). float f = someStruct;getFloat(). someStruct;setInt(i + 1). someStruct.setFloat(f + 1;0F); return someStruct; } }
  3. 實現將在客戶端和服務器之間傳遞的非原始可序列化 object:
     import java.io.*; public class SomeStruct implements Serializable { private int i = 0; private float f = 0.0F; public SomeStruct(int i, float f) { this.i = i; this.f = f; } public int getInt() { return i; } public float getFloat() { return f; } public void setInt(int i) { this.i = i; } public void setFloat(float f) { this.f = f; } }
  4. 實現服務器:
     import java.rmi.*; import java.rmi.server.*; import java.rmi.registry.Registry; import java.rmi.registry.LocateRegistry; import java.net.*; import java.io.*; public class SomeServer { public static void main(String args[]) { String portNum = "1234", registryURL; try{ SomeImpl exportedObj = new SomeImpl(); startRegistry( Integer.parseInt(portNum) ); // register the object under the name "some" registryURL = "rmi://localhost:" + portNum + "/some"; Naming.rebind(registryURL, exportedObj); System.out.println("Some Server ready."); } catch (Exception re) { System.out.println("Exception in SomeServer.main: " + re); } } // This method starts a RMI registry on the local host, if it // does not already exist at the specified port number. private static void startRegistry(int rmiPortNum) throws RemoteException{ try { Registry registry = LocateRegistry.getRegistry(rmiPortNum); registry.list( ); // The above call will throw an exception // if the registry does not already exist } catch (RemoteException ex) { // No valid registry at that port. System.out.println("RMI registry is not located at port " + rmiPortNum); Registry registry = LocateRegistry.createRegistry(rmiPortNum); System.out.println("RMI registry created at port " + rmiPortNum); } } }
  5. 實現客戶端:
     import java.io.*; import java.rmi.*; import java.rmi.registry.Registry; import java.rmi.registry.LocateRegistry; public class SomeClient { public static void main(String args[]) { try { String hostName; String portNum = "1234"; String registryURL = "rmi://localhost:" + portNum + "/some"; SomeInterface h = (SomeInterface)Naming.lookup(registryURL); // invoke the remote method(s) String message = h.someMethod1(); System.out.println(message); int i = h.someMethod2(12344); System.out.println(i); SomeStruct someStructOut = new SomeStruct(10, 100.0F); SomeStruct someStructIn = new SomeStruct(0, 0.0F); someStructIn = h.someStructTest(someStructOut); System.out.println( someStructIn.getInt() ); System.out.println( someStructIn.getFloat() ); } catch (Exception ex) { ex.printStackTrace(); } } }

一個較大的客戶端-服務器應用程序應該分為三個模塊: clientservercommon (用於服務器和客戶端代碼之間共享的類,即本例中的遠程接口和非原始 object)。 然后,客戶端應用程序將從類路徑上的client + common模塊創建,而服務器則從類路徑上的server + common模塊創建。

多年前我用這個例子來學習 RMI 的基礎知識,它仍然有效。 然而它遠非完美(默認 Java package 使用,錯誤的異常處理,主機名和端口參數是硬編碼且不可配置等)

盡管如此,它對初學者來說還是有好處的。 所有文件都可以放在一個目錄中,並使用簡單的javac *.java命令進行編譯。 然后可以使用java SomeServer啟動服務器應用程序,並通過啟動java SomeClient命令啟動客戶端應用程序。

我希望這有助於理解 Java RMI,事實上,它遠比這復雜得多。

您不應該生成存根(如果您正在學習教程,那么它已經過時了)。 可以運行客戶端而不必在本地使用 jars(使用遠程類加載),但是使用本地可用的jars更容易做到這一點(我個人做了相當多的 RMI 並且從未實際部署具有遠程類加載的系統)。 通常,您需要 2 個 jars,一個“客戶端”jar,只有遠程接口(以及這些接口使用的任何可序列化類)和一個“服務器”jar,其中包括實現類。 然后,您將使用服務器 jar 運行服務器,並使用客戶端 jars 運行 rmiregistry/client。

這是一個很好的(最新且簡單的) 入門指南

簡而言之,其他答案的詳細說明是:

  • 客戶端只需要公共接口(和客戶端類),而不需要服務器實現。

  • 服務器需要接口和實現(以及您的服務器主類)。

  • rmiregistry 只需要接口。

    (實際上,您可以在服務器進程中啟動自己的注冊表 - 然后您根本不需要rmiregistry 。請查看java.rmi.registry.LocateRegistry class 中的createRegistry方法。)

這里的“接口”是指遠程接口和它們用作參數或參數類型的任何(可序列化)類。

如何將這些類分發到 jar 文件與此無關。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM