0

2日間のグーグル、スタックオーバーフロー、および他のフォーラムでの回答の検索の後、私はついにあきらめ、ここで回答を見つけたいと思っています!

RMI のよく知られた問題のように見えますが (または、単にほんの少し欠けているだけかもしれません)、単純な RMI クライアント/サーバー アプリケーションを実行する際に厄介な問題があります。

依存関係の管理に Eclipse と m2eclipse (Maven 統合) を使用しています。私のプロジェクト構造は次のとおりです: - クライアント: クライアント コード、共有への依存 - サーバー: サーバー コード、共有への依存 - 共有: サーバーとクライアント間のリモート インターフェイスと共有クラスサーバ

共有:

クライアントが呼び出すことができるリモート インターフェイスは次のとおりです。

public interface IServerReceiver extends Remote, Serializable {
  void connect(long clientId) throws RemoteException;
}

このインターフェースは、クライアントとサーバーの両方が依存関係を持つ共有プロジェクトにあるため、クライアントの classpath にあります

サーバ側:

Server クラスは、レジストリの作成とリモート オブジェクトのエクスポートを担当します (ServerReceiver は IServerReceiver を実装します)。

registry = LocateRegistry.createRegistry(rmiRegistryPort);
serverReceiver = new ServerReceiver(rmiRegistryPort);

registry.rebind(IConstants.RMI_SERVER_RECEIVER_ID, UnicastRemoteObject.exportObject(serverReceiver, 0));

サーバーの起動時に、 -Djava.security.policy を定義することにより、サーバーの JVM にセキュリティ ポリシー ファイルが提供されます (ただし、これがここで説明する問題と関係があるとは思いません)。

クライアント側

クライアントをサーバーに接続する役割を担うクラスは、ServerConnector クラスです。

...
public ServerConnector() {
 ...
 registry = LocateRegistry.getRegistry(serverRMIHost, serverRMIPort);
 serverReceiver = (IServerReceiver) registry.lookup(IConstants.RMI_SERVER_RECEIVER_ID);
 serverReceiver.connect(client.getId());
 ...
}

Client クラスのコンストラクタは次のようになります (一部の行は省略されています)。

public Client(..) {
 ....
 serverConnector = new ServerConnector(serverRMIHost, serverRMIPort, this);
 ....
}

サーバーだけでなく、クライアントもセキュリティ ポリシー ファイルを取得します。

テスト

サーバーを作成してからクライアントを作成する統合テストを開始すると、すべてが期待どおりに機能し、connect() メソッドが期待どおりの結果を返します。

ただし、サーバーインスタンスを通常どおり起動しようとすると、サーバーに接続しようとするクライアントインスタンスを起動すると、次の例外がスローされます。

Caused by: java.rmi.UnmarshalException: error unmarshalling return; nested exception is: 
java.lang.ClassNotFoundException: assign2.comm.ServerReceiver_Stub
at sun.rmi.registry.RegistryImpl_Stub.lookup(Unknown Source)
at assign2.comm.ServerConnector.<init>(ServerConnector.java:65)

答えを探しているうちに最も顕著だったのは、クライアントがサーバー インターフェイス定義 (IServerReceiver) にアクセスできる必要があるということでした。ただし、適切な依存関係が整っているため、ここではそのように思えます (クライアント -> 共有、サーバー -> 共有、IServerReceiver は共有に配置されます)。私がよく理解していない例外の別の側面は次のとおりです。JDK 1.5 以降、rmic によるスタブ生成は不要になり、動的プロキシを介して実現されます。したがって、クライアントは、動的プロキシを作成するためにインターフェイス IServerReceiver にアクセスするだけで済みます。では、なぜ彼は ServerReceiver_Stub クラスがまったく見つからないことに文句を言うのでしょうか?

統合テストは正常に実行され、そのテスト内でクライアントは IServerReceiver インターフェースの実装にもアクセスできるため、クライアントはリモート インターフェースだけでなくその実装にもアクセスする必要があると想定してもよろしいですか?

私は今かなり混乱しているので、この問題に光を当てるのを手伝ってもらえますか? 長い記事を読んでくれてありがとう!;-)

乾杯、クリス

4

2 に答える 2

1

IServerReceiverしてはいけませSerializable。私の推測では、サーバー インスタンスがプロキシではなくネットワーク経由で送信されていると思われます。

于 2011-05-17T02:55:16.363 に答える
1

オブジェクトをエクスポートするときにポート番号を提供しているので、スタブは必要ありません。唯一の説明は、生成されたスタブが動的スタブではなくバインドされたことです。存在するスタブ ファイルを削除して、再テストします。サーバーがスタブを必要としていてスタブを持っていない場合、誰かがそれを調べたときではなく、エクスポート時に失敗するはずです。

クライアントがリモート インターフェイスへのアクセスだけでなく、その実装も必要としていると想定するのは正しいでしょうか?

いいえ。スタブのみが必要です。実装はリモートです。それが全体のアイデアです。ただし、スタブが依存するクラス、つまりリモート インターフェイスとそれが依存するクラスなども必要です。

さらに質問があります。

  1. レジストリ ポートが新しい ServerReceiver(rmiRegistryPort) に提供されるのはなぜですか?
  2. ServerReceiver は UnicastRemoteObject を拡張していないと思いますか?
  3. これが実行中の実際のサーバー コードであると確信していますか?
于 2011-05-17T02:33:10.023 に答える