2013-01-21 113 views
1

我有許多客戶端連接到的RMI服務器。當客戶註冊更改時,服務器會作出反應並指示所有客戶進行更改。如何在沒有註冊客戶端的情況下製作RMI服務器多客戶端程序

我一直在尋找一些hello world RMI的例子,但沒有一個解決了如何從服務器持久地連接客戶端。

我想實現的是沿着這些方向的東西: 服務器在rmiregistry上註冊。 客戶端1連接到服務器並調用服務器上的方法。 客戶端2連接到服務器並調用服務器上的方法。 服務器將更改從client2發送到client1。

如何在不將每個客戶端註冊爲服務器的情況下實現這一目標?

編輯:沒有找到stub類: 看着幾個優秀ansvers的(奉承奉承)我遇到以下異常 java.rmi.StubNotFoundException後thegame.connectivity.GameClient_Stub;嵌套的例外是:

java.lang.ClassNotFoundException: thegame.connectivity.GameClient_Stub 
    at sun.rmi.server.Util.createStub(Util.java:292) 
    at sun.rmi.server.Util.createProxy(Util.java:140) 
    at sun.rmi.server.UnicastServerRef.exportObject(UnicastServerRef.java:196) 
    at java.rmi.server.UnicastRemoteObject.exportObject(UnicastRemoteObject.java:310) 
    at java.rmi.server.UnicastRemoteObject.exportObject(UnicastRemoteObject.java:237) 
    at thegame.connectivity.GameClient.bind(GameClient.java:37) 
    at thegame.connectivity.GameClient.run(GameClient.java:51) 
    at thegame.connectivity.GameClient.main(GameClient.java:29) 
Caused by: java.lang.ClassNotFoundException: thegame.connectivity.GameClient_Stub 
    at java.net.URLClassLoader$1.run(URLClassLoader.java:366) 
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354) 
    at java.lang.ClassLoader.loadClass(ClassLoader.java:423) 
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308) 
    at java.lang.ClassLoader.loadClass(ClassLoader.java:356) 
    at java.lang.Class.forName0(Native Method) 
    at java.lang.Class.forName(Class.java:264) 
    at sun.rmi.server.Util.createStub(Util.java:286) 
    ... 7 more 

在這個類

public class GameClient extends Thread implements Remote, Client, ModelChangeListener<Client>{ 
    private static final long serialVersionUID = -394039736555035873L; 
    protected Queue<GameModelEvent> queue = new ConcurrentLinkedQueue<GameModelEvent>(); 


    public GameClient(){ 

    } 

    public static void main(String[] args){ 
     GameClient client = new GameClient(); 
     client.run(); 
    } 


    protected void bind(){ 
     System.setProperty("java.rmi.server.codebase","file:bin/"); 
     try { 
      Registry registry = LocateRegistry.getRegistry(); 
      Client c = (Client)UnicastRemoteObject.exportObject(this); 
      Server stub = (Server) registry.lookup("Server"); 
      stub.registerClient(c); 
     } catch (RemoteException | NotBoundException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
    } 



    @Override 
    public void run() { 
     super.run(); 
     bind(); 
     while(!Thread.interrupted()){ 
      System.out.print("."); 
      GameModelEvent event = queue.poll(); 
      while(event != null){ 

       System.out.println(event); 


       event = queue.poll(); 
      } 

      try { 
       Thread.sleep(100); 
      } catch (InterruptedException e) { 
       return; 
      } 
     } 

    } 
... 

它運作良好,當我註釋掉UnicatRemoteObject,只是傳遞null作爲參數。所以有一個連接,一切都在運行。但沒有存根... 註釋掉代碼庫也沒有效果。代碼庫在服務器上正常工作。

有什麼不對?

+0

這可能幫助:http://java.sys-con.com/node/46979 –

回答

1

客戶端不僅可以與服務器通話,還可以直接與對方通話。

在客戶端上註冊的客戶,建立像樹結構,從少數「主客戶」的開始,只有這些被註冊到服務器。他們可以將消息級聯到其他子客戶端,因此可能會支持大量的客戶端,只有很少的客戶端可以直接在服務器上註冊。

不幸的是,這種方法的客戶端必須被允許有一個正常關機,使他們能夠分配的職責有關,他們目前有其他節點的子客戶端,或返回到服務器。由於整個樹枝將被切斷,因此不能簡單地殺死它們。也許如果客戶覺得不再與主人聯繫,它應該吸引服務器並獲得另一個。

又見對documentation可以做到的。

也不要致電exportObject(Remote obj),因爲使用它需要一個存根類(它返回類的一個實例,RemoteStub)。使用返回接口的exportObject(Remote obj, int port)。也涵蓋here

+0

你如何註冊客戶? –

+0

客戶端可以在服務器上調用傳遞self作爲參數的遠程方法。服務器稍後可以將此參考傳遞給其他客戶端,以便他們可以直接交談。您可以像在同一個jre上的普通對象一樣輕鬆地傳遞RMI句柄。 – h22

+0

Audrius:我試圖做一個回調監聽器,但我得到一個討厭的異常。任何想法有什麼不對?我用細節更新了問題 –

2

當您的客戶首次聯繫RMI服務器時,他們可以提供某種實現Remote接口的偵聽器。服務器可以維護一組這些RemoteListener,並且每當客戶端被通知某個事件時,它可以調用您想要創建的任何remoteAction(ActionEvent e)。在不瞭解更多關於您的問題域的情況下,很難具體。

+0

你能給我帶些API或實例的線路提示嗎? –

+0

什麼是「RemoteListener」? –

+0

每個客戶端都會收到其特定的偵聽器對象。 client1調用發送消息方法,即rmi服務器發送給客戶機2的偵聽器。 –

0

答第I部分)

要與純粹的RMI做到這一點,客戶端將能夠充當一個RMI服務器回調的目的 - 有根本沒有得到來自客場,這是怎麼了RMI協議作品。

如果您不希望每個客戶端都以這種方式運行,那麼您需要在網絡堆棧中下拉一個級別並實現您自己的套接字協議來代替RMI。當服務器收到註冊消息時,它需要保持由客戶端根據需要將數據通過該套接字推回客戶端的套接字連接。

回答第二部分(繼編輯的問題))

你需要,當你使用UnicastRemoteObject.exportObject(Remote)當從JDK源以下注釋指示導出客戶端已經預生成存根:

使用UnicastServerRef構造函數傳遞布爾值true表示應該只使用生成的存根類。必須使用生成的存根類而不是動態代理,因爲此方法的返回值是動態代理類無法擴展的RemoteStub。

因此,java.lang.ClassNotFoundException

那麼,要麼或致電UnicastRemoteObject.exportObject(Remote, int)

您還需要存根綁定到註冊表,方法是使用指定服務名稱與Registry.bind(String, Remote)調用。

這實際上是運行客戶端作爲RMI服務器 - 正如我在我的答案'答案第一部分'中所說的,這是使用RMI時可以工作的唯一方法 - 在那裏進行遠程方法調用的RMI, 。