天天看點

《Thinking in Java》RMI遠端方法示例代碼運作是可能遇到的問題及解決方法

轉自:http://ajava.org/course/net/541.html

在Think In Java裡有一個示例 RMI 的代碼 PerfectTime, 可是其中并沒有把有些執行細節說明了,也就可以造成新手運作它會出某些異常而不知所措。下面将列出可能産生的異常,并說明解決辦法,如讀者在執行當中還遇到其他未列出的異常,可留言告知,吾将盡力而為。

首先把代碼PerfectTime和DisplayPerfectTime 中的//colossus:2005/PerfectTime改為//localhost:2005/PerfectTime ,因為colossus為機器名,是以改為localhost指向本機,不然找不到主機colossus的。

使用rmic編譯時,要是使用rmic -v1.1 -keep XXXImpl 才會生成_Skel and _Stub;我在jdk1.5環境下直接使用rmic XXXImpl之生成了_Stub;

已經用指令 RMIC 生成PerfectTime_Stub.class,并且執行了指令 rmiregistry 2005

1. 執行java PerfectTime出現異常 java.security.AccessControlException: access denied (java.net.SocketPermission 127.0.0.1:2005 connect,resolve)

無法解析和連接配接到127.0.0.1的2005端口上,原因是在PerfectTime中設定了安全管理器<System.setSecurityManager(new RMISecurityManager());>,可是又沒有設定通路的政策,解決辦法有四(解決這種異常的辦法同樣适用于DisplayPerfectTime):

(1) 可以把代碼System.setSecurityManager(new RMISecurityManager());去掉,不設定安全管理器

(2) 修改JRE的安全政策檔案,這就要求你能确定執行時是用的哪個JRE,比如在Eclipse中用JDK是c:/Java/jdk1.5.0_06,相應的安全政策檔案就是c:/java/jdk1.5.0_06/jre/lib/security/java.policy,如果是Applet中的java程式就應該是在 jre 目錄中,如檔案C:/Java/jre1.5.0_06/lib/security/java.policy。修改安全政策檔案,在grant {},大括号中加上permission java.net.SocketPermission "localhost:2005","connect,resolve";

(3) 建立自己的政策檔案,如c:/MyPolicy.policy ,内容為:

  1. grant {    
  2. permission java.net.SocketPermission "localhost:2005","connect,resolve";    
  3. }   

執行PerfectTime時用指令 java -Djava.security.policy=c:/MyPolicy.policy PerfectTime  指定了安全政策檔案

(4) 把 System.setSecurityManager (new RMISecurityManager()) 改為匿名類實作,覆寫兩個方法

  1. System.setSecurityManager (new RMISecurityManager() {    
  2. public void checkConnect (String host, int port) {}    
  3. public void checkConnect (String host, int port, Object context) {}    
  4. });   

當然最簡單的解決方法莫過于第一種。

2. 同樣是執行 PerfectTime 出現的異常

  1. java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:    
  2. java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:    
  3. java.lang.ClassNotFoundException: PerfectTime_Stub   

很多人對這個問題有些莫名其妙,因為明明看到 PerfectTime_Stub 和 PerfectTime 這兩個類是在同一個目錄中,并且classpath 也有設定目前目錄,按理既然能加載 PerfectTime 類執行,就能加載到 PerfectTime_Stub吧,為什麼還提示ClassNotFound呢?其實類 PerfectTime_Stub并非由PerfectTime執行行直接加載,而是PerfectTime在向RMI注冊時,要求rmiregistry去加載 PerfectTime_Stub類的,了解了這一層次上的意義就會知道其實 PerfectTime_Stub是為 rmiregistry所用的。是以解決辦法是:

(1) 在執行 rmiregistry 之前,設定classpath讓能查找到PerfectTime_Stub類,如在同一Dos視窗中,假設 PerfectTime_Stub類是在E:/workspace/TestRMI/bin目錄中,執行過程那就是

C:/Documents and Settings/unmi>set classpath=%classpath%;E:/workspace/TestRMI/bin

C:/Documents and Settings/unmi>rmiregistry 2005

(2) 或者在指令行中先進入到 PerfectTime_Stub類所在的目錄,然後再執行 rmiregistry (這種方法實質是與上面一樣的,隻是恰當的應用的classpath中的目前目錄 "." ),執行過程如下

C:/Documents and Settings/unmi>e:

E:/>cd E:/workspace/TestRMI/bin

E:/workspace/TestRMI/bin>rmiregistry 2005

參看:rmiregistry was finding the stubs in its CLASSPATH

3. 執行用戶端程式 DisplayPerfectTime 出現異常 java.security.AccessControlException: access denied (java.net.SocketPermission 127.0.0.1:1276 connect,resolve),同時在伺服器端也産生異常 Exception in thread "RMI TCP Connection(6)-127.0.0.1" java.security.AccessControlException: access denied (java.net.SocketPermission 127.0.0.1:1296 accept,resolve)

直接能想到的解決辦法是把127.0.0.1:1276,127.0.0.1:1276的解析連接配接權限也加上,方法可取第 1 種異常所列的方法,但這個端口是随機的。在此解析一下這些端口的用途,2005是直接指定的供用戶端查找注冊的服務對象引用的端口,這是固定的,而上面産生的在用戶端和伺服器上的1276和1296的端口,是随機的,是在方法調用時真正的用戶端與提供服務的伺服器(而非注冊伺服器)之間的資料通信的端口。

為了滿足上面的端口應用,可以在安全政策檔案中隻加上 permission java.net.SocketPermission "localhost:*","accept,connect,resolve"; 允許在所有端口上的接受,連接配接,解析。再如果要通路的IP很多,又要寫成 permission java.net.SocketPermission "*:*","accept,connect,resolve"; 友善。

4. 執行用戶端程式 DisplayPerfectTime出現異常  java.rmi.UnmarshalException: Error unmarshaling return header; nested exception is: java.io.EOFException,這種異常應該比較少見,出現情況是 用戶端有權限通路服務提供端的某個端口,而服務提供端卻無權限在某個端口上或給那個用戶端提供服務造成的,解決辦法把用戶端和伺服器的安全政策檔案都改為能通路任何端口就行。

總結:上面1、3、4三種情況都是因為權限不足所造成的,如果安全控制的粒度不要求太細的化,在伺服器端和用戶端可以不用設定定全管理器,或者政策檔案中設定為能接受、連接配接、解析任何IP及端口:permission java.net.SocketPermission "*:*","accept,connect,resolve"; 或者用1(4)的方法忽略所有IP及端口的檢測。