RMI が特定のポートのセットのみを使用するようにするにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/56687

  •  09-06-2019
  •  | 
  •  

質問

私たちのアプリケーションでは、クライアントとサーバー間の通信に非常に異なる方法で RMI を使用しています。

  1. データをサーバーからクライアントにプッシュして表示します。
  2. クライアントからサーバーに制御情報を送信します。
  3. これらの制御メッセージからのコールバックは、サーバーからクライアントに戻るコード パスをコードします (追記 - これは一部のレガシー コードの副作用であり、私たちの長期的な意図ではありません)。

私たちがやりたいのは、RMI 関連のすべてのコードが既知の指定されたポートのインベントリのみを使用するようにすることです。これには、レジストリ ポート (通常は 1099 であると予想されます)、サーバー ポート、およびコールバックから得られるポートが含まれます。

私たちがすでに知っていることは次のとおりです。

  1. LocateRegistry.getRegistry(1099) または Locate.createRegistry(1099) は、レジストリが 1099 をリッスンしていることを確認します。
  2. UnicastRemoteObject コンストラクター/exportObject 静的メソッドを port 引数とともに使用すると、サーバー ポートが指定されます。

こういった点もこの記事で取り上げています サンフォーラムの投稿.

私たちが知らないことは次のとおりです。コールバックの結果としてサーバーに戻るクライアント接続が、デフォルトで匿名ポートに接続されるのではなく、指定されたポートにのみ接続されるようにするにはどうすればよいでしょうか?

編集:私の発見と問題の解決方法を要約した長い回答を追加しました。これが同様の問題を抱えている他の人の助けになれば幸いです。

2 番目の編集:私のアプリケーションでは、ソケット ファクトリの作成と変更に競合状態があるようです。ユーザーが Beanshell スクリプトでデフォルト設定をオーバーライドできるようにしたいと考えていました。残念なことに、ファクトリによって最初のソケットが作成された後、私のスクリプトが大幅に実行されているようです。その結果、一連のデフォルトとユーザー設定からのポートが混在することになります。この質問の範囲外ではさらに多くの作業が必要になりますが、いつかこの問題に取り組む必要があるかもしれない他の人にとって興味深い点として指摘しておきたいと思います。

役に立ちましたか?

解決

これは、カスタム RMI ソケット ファクトリを使用して実行できます。

ソケット ファクトリは、RMI がクライアント側とサーバー側の両方で使用するソケットを作成するため、独自に作成した場合は、使用されるポートを完全に制御できます。クライアント ファクトリはサーバー上で作成され、シリアル化されてからクライアントに送信されます。これは非常に適切です。

その方法を説明する Sun のガイドを次に示します。

他のヒント

これにはソケット ファクトリは必要ありません。さらには複数のポートも必要ありません。サーバー JVM からレジストリを起動している場合は、すべてにポート 1099 を使用できます。実際、デフォルトでこれが行われます。クライアント コールバック オブジェクトのように、レジストリをまったく開始しない場合は、レジストリをエクスポートするときにポート 1099 を指定できます。

質問の「コールバックによるサーバーへのクライアント接続」に関する部分は意味がありません。これらはサーバーへの元のクライアント接続と何ら変わりはなく、同じサーバー ポートを使用します。

以下の長い回答の要約:私が抱えていた問題 (RMI 接続の両端でのサーバー ポートとコールバック ポートの制限) を解決するには、クライアントとサーバーのソケット ファクトリのペアを 2 つ作成する必要がありました。

さらに長い答えが続きます。

コールバック問題に対する私たちの解決策は、基本的に 3 つの部分から構成されていました。1 つ目はオブジェクトのラッピングで、クライアントからサーバーへの接続とサーバー間の接続に使用されていることを指定する機能が必要でした。サーバーからクライアントへのコールバックに使用されます。の拡張機能を使用する UnicastRemoteObject これにより、使用したいクライアントおよびサーバーのソケット ファクトリを指定できるようになりました。ただし、ソケット ファクトリをロックダウンする最適な場所は、リモート オブジェクトのコンストラクター内です。

public class RemoteObjectWrapped extends UnicastRemoteObject {
// ....
private RemoteObjectWrapped(final boolean callback) throws RemoteException {
  super((callback ? RemoteConnectionParameters.getCallbackPort() : RemoteConnectionParameters.getServerSidePort()),
        (callback ? CALLBACK_CLIENT_SOCKET_FACTORY : CLIENT_SOCKET_FACTORY),
        (callback ? CALLBACK_SERVER_SOCKET_FACTORY : SERVER_SOCKET_FACTORY));
}
// ....
}

したがって、最初の引数は、オブジェクトがリクエストを予期している部分を指定し、2 番目と 3 番目の引数は、このリモート オブジェクトを駆動する接続の両端で使用されるソケット ファクトリを指定します。

接続で使用されるポートを制限したいため、RMI ソケット ファクトリを拡張してポートをロックダウンする必要がありました。以下に、サーバーおよびクライアント ファクトリのスケッチをいくつか示します。

public class SpecifiedServerSocketFactory implements RMIServerSocketFactory {
/** Always use this port when specified. */
private int serverPort;
/**
 * @param ignoredPort This port is ignored.  
 * @return a {@link ServerSocket} if we managed to create one on the correct port.
 * @throws java.io.IOException
 */
@Override
public ServerSocket createServerSocket(final int ignoredPort) throws IOException {
    try {
        final ServerSocket serverSocket = new ServerSocket(this.serverPort);
        return serverSocket;
    } catch (IOException ioe) {
        throw new IOException("Failed to open server socket on port " + serverPort, ioe);
    }
}
// ....
}

上記のサーバー ソケット ファクトリでは、以前に指定したポートのみがこのファクトリで使用されることが保証されることに注意してください。クライアント ソケット ファクトリは、適切なソケット ファクトリとペアにする必要があります (そうしないと接続できません)。

public class SpecifiedClientSocketFactory implements RMIClientSocketFactory, Serializable {
/** Serialization hint */
public static final long serialVersionUID = 1L;
/** This is the remote port to which we will always connect. */
private int remotePort;
/** Storing the host just for reference. */
private String remoteHost = "HOST NOT YET SET";
// ....
/**
 * @param host The host to which we are trying to connect
 * @param ignoredPort This port is ignored.  
 * @return A new Socket if we managed to create one to the host.
 * @throws java.io.IOException
 */
@Override
public Socket createSocket(final String host, final int ignoredPort) throws IOException {
    try {
        final Socket socket = new Socket(host, remotePort);
        this.remoteHost = host;
        return socket;
    } catch (IOException ioe) {
        throw new IOException("Failed to open a socket back to host " + host + " on port " + remotePort, ioe);
    }
}
// ....
}

したがって、双方向接続を強制的に同じポート セットに維持するために残っている唯一のものは、クライアント側にコールバックしていることを認識するロジックです。その場合は、リモート オブジェクトのファクトリ メソッドがコールバック パラメーターを true に設定して RemoteObjectWrapper コンストラクターを呼び出すようにしてください。

クライアント コールバックを使用した RMI サーバー/クライアント アーキテクチャを実装する際に、さまざまな問題が発生しました。私のシナリオは、サーバーとクライアントの両方がファイアウォール/NAT の背後にあるというものです。最終的に、完全に動作する実装が得られました。私が行った主な作業は次のとおりです。

サーバー側、ローカルIP:192.168.1.10。パブリック (インターネット) IP 80.80.80.10

ファイアウォール/ルーター/ローカル サーバー PC でポート 6620 を開きます。ファイアウォール/ルーター/ローカル サーバー PC でポート 1099 を開きます。ルーター/NATで、ポート6620から192.168.1.10:6620のRouter/Natのリダイレクト接続をリダイレクトポート1099から192.168.1.10:1099のルーター/NATリダイレクト着信接続

実際のプログラムでは:

System.getProperties().put("java.rmi.server.hostname", IP 80.80.80.10);
MyService rmiserver = new MyService();
MyService stub = (MyService) UnicastRemoteObject.exportObject(rmiserver, 6620);
LocateRegistry.createRegistry(1099);
Registry registry = LocateRegistry.getRegistry();
registry.rebind("FAManagerService", stub);

クライアント側、ローカル IP:10.0.1.123 パブリック (インターネット) IP 70.70.70.20

ファイアウォール/ルーター/ローカル サーバー PC でポート 1999 を開きます。ルーター/NAT では、ポート 1999 の受信接続を 10.0.1.123:1999 にリダイレクトします。

実際のプログラムでは:

System.getProperties().put("java.rmi.server.hostname", 70.70.70.20);
UnicastRemoteObject.exportObject(this, 1999);
MyService server = (MyService) Naming.lookup("rmi://" + serverIP + "/MyService ");

お役に立てれば。イラクリス

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top