天天看點

Java遠端方法調用RMI的實作

                                   RMI的實作

遠端方法調用基本流程

     遠端方法,可以讓本地通路遠端的對象。當需要通路遠端對象的時候,一般需要在用戶端或者客戶堆中建立一些輔助對象,這些輔助對象使得客戶感覺如同調用本地對象的方法一樣。客戶輔助對象喬裝為服務對象,當客戶調用客戶輔助對象上的方法,客戶輔助對象會聯系伺服器,傳送方法調用資訊(方法名稱,變量等),然後就等待伺服器的傳回。

    在伺服器端,服務輔助對象從客戶輔助對象中接受請求(Socket連接配接),将調用的資訊解包,然後調用伺服器端上的真正服務對象的真正方法。對于服務對象來說,調用是本地的。

    服務輔助對象從服務中得到傳回值,将它打包,然後就運回到客戶輔助對象,客戶輔助對象對資訊解包,最後将傳回值交給客戶對象。

RMI概況

    RMI提供了客戶輔助對象和服務輔助對象,為客戶輔助對象和服務對象建立相同的方法。由于調用遠端方法的過程中使用的是I/O和網絡,而這些都有可能發生異常。

   RMI将客戶輔助對象成為stub(樁),服務輔助對象稱為skeleton(骨架)。

制作遠端服務

   步驟一 制作遠端接口:定義了可以供客戶遠端調用的方法。stub和實際服務都實作該接口

   步驟二 制作遠端接口的實作:真正的實際工作的服務

   步驟三 利用rmic産生stub

   步驟四 啟動RMI:registry,rmiregistry如同一個清單,客戶可以從中查到代理的位置

   步驟五 開始啟動遠端服務:開始啟動遠端服務,一般服務實作類會去執行個體化一個服務執行個體,然後将這個服務注冊到RMI registry。

   步驟一 制作遠端接口

   一般首先擴充自Remote,Remote僅僅是一個記号接口,沒有方法,在該接口中聲明所有的方法都會抛出RemoteException,必須确定變量和傳回值是原語性或者可序列化的,如果是複雜對象則需要對于負責對象先序列化。即讓該複雜對象繼承Serializable。

   步驟二 制作遠端接口的實作

   具體的服務必須實作遠端接口,同時一般直接擴充UnicastRemoteObject,設計一個不帶變量的構造器,同時聲明RemoteException。(當類被執行個體化的時候,超類的構造器總是先被調用,如果超類的構造器抛出了異常,那麼在子類的構造器中必須抛出異常,而UnicastRemoteObject的構造器抛出了異常。)可以在實作類中注冊該服務,但是必須要先啟動rmiregistry。 Naming.rebind("name",myremoteImpl)注冊到rmiregistry中。

   步驟三 産生stub

   rmic 遠端實作類名

   步驟四 執行rmiregistry

   步驟五 啟動服務

   啟動服務,一般可以在實作類的内部啟動或者單獨類中啟動,主要就是注冊到rmiregistry

在遠端虛拟機上啟動伺服器一般要包括兩個伺服器,一個是遠端對象本身,還有一個是允許本地用戶端下載下傳遠端對象引用的系統資料庫。必須要先執行rmiregistry目的開啟系統資料庫,然後再啟動遠端對象注冊到該系統資料庫中

  以上是伺服器端的配置,在用戶端,隻需要該遠端對象接口的類或者.class檔案,以及利用rmic生成的stud檔案。

代碼執行個體

遠端接口

import java.rmi.Remote;

import java.rmi.RemoteException;

public interface MyRemote extends Remote {

        public String sayHelloBaby() throws RemoteException;

}

遠端接口實作

import java.rmi.server.UnicastRemoteObject;

public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote {

  protected MyRemoteImpl() throws RemoteException {

  }

  @Override

  public String sayHelloBaby() throws RemoteException {

    // TODO Auto-generated method stub

    return "Congratulations to you!";

啟動遠端服務,必須先執行rmiregistry,

在windows下執行start  rmiregistry

import java.io.IOException;

import java.rmi.Naming;

public class RMIServer {

  public static void main(String[] args) {

                try{

                  MyRemote f=new MyRemoteImpl();

                  Naming.rebind("myfirst", f);

                  System.out.println("OK");

                }catch(IOException e)

                {

                  System.err.println(e);

                }

用戶端服務

public class MyRemoteClient {

    public static void main(String[] args) {

        // TODO Auto-generated method stub

         try

          {

               MyRemote service=(MyRemote)Naming.lookup("rmi://127.0.0.1/FirstRemote");

               String res=service.sayHelloBaby();

               System.out.println("return="+res);

            }catch(Exception e)

             {

               e.printStackTrace();

           }

    }

整個目錄截圖如下

用戶端所需要類:F:\JavaRMI\client

伺服器所需要類:F:\JavaRMI\server

執行順序是:

第一步:編譯所有的類,在此要将遠端對象接口複制到用戶端下

第二步:執行rmic MyRemoteImpl,這個目的生成樁位元組碼

第三步:将該樁位元組碼複制一份到用戶端目錄下

第四步:啟動rmiregistry服務,必須要保證樁位元組碼在classpath路徑下

本次樁位元組碼在F:\JavaRMI\server,是以啟動rmiregistry之前先設定路徑

第五步:啟動遠端對象服務即RMIServer

第六步:執行用戶端程式

在使用RMI的過程中,必須要注意:

1) 在啟動遠端服務之前先必須要啟動rmiregistry,必須要保證樁在類路徑中,

   可以在啟動rmiregistry之前設定

2)必須要讓變量的傳回值的類型成為可序列化的類型

3)用戶端必須要有Stub類和遠端對象接口類。

問題解決:

在執行伺服器遠端對象運作的中,可能會出現java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:

這是因為在執行start rmiregistry 指令前必須確定 MyRemoteImpl_Stub.class(留意包

名)包含在了CLASSPATH配置的路徑中,是以必須在在執行start rmiregistry指令前執行  

set CLASSPATH=%CLASSPATH%;F:\JavaRMI\server

本文轉自 zhao_xiao_long 51CTO部落格,原文連結:http://blog.51cto.com/computerdragon/1178053