-1

一見、これは基本的なネットワークの問題のように思えるかもしれませんが、そうではないと思います。Java 自体が RMI 接続を「無視」しているようです。

Windows 10 PC 上のすべての Java 8u101。

LAN 上の RMI を介して通信する、非常に成熟した堅牢な分散 Java アプリケーションがあります。
長年にわたり LAN 上で当社のアプリケーションを実行してきたお客様にとって、まったく問題はありませんでした。この同じアプリケーションは、(RMI サーバーと同じ「サーバー」PC 上の) 共有データベースにも接続します。

私たちの顧客の 1 人は最近、おそらく 100 メートル離れた場所に新しい建物を建設し、その 2 つの施設は WAN を介して接続されています。ただし、リモート サイトの Java クライアントは Java RMI サーバーに接続できません。

以下のように、ネットワーキング自体はすべてOKのようです....

  • LAN では、すべてが通常どおり完全に実行されます
  • 非 RMI は問題ありません:
    • リモートの場所から別のデータベース クライアントを実行し、(RMI サーバーと同じ PC 上で) データベース サーバーに正常に接続できます。
  • RMI ポートは、クライアント エンドからは正常に見えます
    • リモート ロケーションで telnet クライアントを使用して、RMI ポートへの接続を確立できます。
  • RMI ポートはサーバー PC で接続されているようです。
    • Java RMI サーバー自体を実行している PC では、「netstat -an」を介して接続を監視できます。これは、適切な WAN IP アドレスから到達する確立された接続を示します。

これは、接続が正しい場所に到達しており、構成の誤りやファイアウォールなどによってブロックされていないことを (私には) 示しています。RMI サーバー PC に「入る」ように見えますが、RMI サーバー自体には到達しません。

私の最初の推測では、「Java コントロール パネル」のようなものを使用して、おそらく RMI トラフィックを制限している可能性のあるセキュリティ設定を調整することでしたが、そのようなスイッチを認識できません。

誰でもこれに関する手がかりを提供できますか?

  • 接続がJavaに到達することを確認する方法はありますか?
  • RMI サーバーに渡される前にこれをブロックしている Java セキュリティでしょうか?

提案やガイダンスをいただければ幸いです。

(編集、詳細を追加...)

もう少し情報は次のとおりです...

  • LAN内では、サーバーPCは192.168.0.110として知られています
  • 遠隔地から (WAN 経由で) サーバー PC は 110.142.83.167 として知られています。
  • リモート クライアントは RMI サーバー オブジェクトを取得しているようです
  • スタック トレース (詳細は後述) には、「接続がホストへの接続を拒否しました: 192.168.0.110;」があります。
  • そのため、どの IP アドレスであるかについて混乱があるようです。クライアントは 110.142.83.167 を要求しましたが、サーバーはそれが 192.168.0.110 であると認識しています

スタックトレースは以下の通りです...

2016-10-18 15:02:11,123 [S38P4][43983  ][AWT-EventQueue-0] WARN  StackTraceLogger log: StackTraceLogger ------- INFO=ClientRMIObject.connectToSentry(): RemoteException: sentryIP=110.142.83.167<<
StackTraceLogger ------ Throwable.MSG=Connection refused to host: 192.168.0.110; nested exception is: 
    java.net.ConnectException: Connection timed out: connect< Throwable=java.rmi.ConnectException: Connection refused to host: 192.168.0.110; nested exception is: 
    java.net.ConnectException: Connection timed out: connect
StackTraceLogger.START TRACE .................................
   sun.rmi.transport.tcp.TCPEndpoint.newSocket(Unknown Source)
   sun.rmi.transport.tcp.TCPChannel.createConnection(Unknown Source)
   sun.rmi.transport.tcp.TCPChannel.newConnection(Unknown Source)
   sun.rmi.server.UnicastRef.invoke(Unknown Source)
   java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(Unknown Source)
   java.rmi.server.RemoteObjectInvocationHandler.invoke(Unknown Source)
   com.sun.proxy.$Proxy13.registerWithSentry(Unknown Source)
   com.my.co.package.ClientRMIObject.connectToSentry(ClientRMIObject.java:198)

コードのスーパーカットダウンバージョン(ボイラープレートなどなし)に続いて...

RMI サーバーで ...

// auto called at server on startup ...
public final class MyServer {
  public final static int SRVR_PORT = 21099;
  public final static String LOOKUP_NAME = "MyLookupName";
  public MyServer() {
      java.rmi.registry.Registry reg = java.rmi.registry.LocateRegistry.createRegistry(SRVR_PORT);
      ServerRMIObject srvrRmiObj = new ServerRMIObject();
      reg.rebind(LOOKUP_NAME, srvrRmiObj);
  }
}

サーバーRMIオブジェクト ...

public final class ServerRMIObject extends java.rmi.server.UnicastRemoteObject {
  private List<ClientRMIObject> clients = ...

  public ServerRMIObject() throws java.rmi.RemoteException {
    super(MyServer.SRVR_PORT);
  }

  public void registerWithSrvr(ClientRMIObject client) {
    logger.info("This line is never run when called from remote location");
    clients.add(client);
  }

  // example method ...
  public void broadcastToClients(Delivery d) {
    for (ClientRMIObject client : clients) {
      client.receiveFromSrvr(d);
    }
  }
}

各 RMI クライアントで ...

public final class ClientRMIObject extends java.rmi.server.UnicastRemoteObject {

  private final int CLIENT_PORT = 21099;
  private final String SRVR_FROM_LAN = "//192.168.0.110:"+MyServer.SRVR_PORT+"/"+MyServer.LOOKUP_NAME; 
  private final String SRVR_FROM_WAN = "//110.142.83.167:"+MyServer.SRVR_PORT+"/"+MyServer.LOOKUP_NAME;
  private ServerRMIObject rmiSrvr = null;

  public ClientRMIObject() {
    super(CLIENT_PORT);
  }

  // auto called at client on startup
  public void start() {
    if (VIA_LAN) {
        rmiSrvr = java.rmi.Naming.lookup(SRVR_FROM_LAN);
    } else if (VIA_WAN) {
        rmiSrvr = java.rmi.Naming.lookup(SRVR_FROM_WAN);
    }

    // this has been successful to here. 

    // the following throws an exception ...
    // java.rmi.ConnectException: Connection refused to host: 192.168.0.110; 
    // please see more exception details above        
    rmiSrvr.registerWithSrvr(this); 

  }

  // example method ...
  public void receiveFromSrvr(Delivery d) {
     ....
  }

  // example broadcast from one client to all clients ...
  public void broadcastToClients(Delivery d) {
    rmiSrvr.broadcastToClients(d);
  }
}
4

2 に答える 2

1

https://docs.oracle.com/javase/8/docs/technotes/guides/rmi/javarmiproperties.htmlから

java.rmi.server.hostname

このプロパティの値は、クライアントがリモート オブジェクトでメソッドを呼び出せるようにするために、ローカルで作成されたリモート オブジェクトのリモート スタブに関連付ける必要があるホスト名文字列を表します。このプロパティのデフォルト値は、ローカル ホストの IP アドレスであり、「ドット四角形」形式です。

RMI レジストリは、クライアントがリモート オブジェクトを呼び出すために接続できる IP アドレス (またはホスト名) とポートをクライアントに報告します。デフォルトでは、ローカル ホストのアドレスを使用します。

サーバーは IP アドレスで構成されている192.168.0.110ため、それが RMI レジストリから報告されます。RMI レジストリは、他の IP アドレス (つまり、パブリックにルーティング可能な IP アドレス) を介してマシンにアクセスする必要があることを認識していません。110.142.83.167また、ファイアウォールがネットワーク アドレス変換を行っているかどうかも認識できません。

会話は次のようになります。

  • WAN クライアントは の RMI レジストリに接続します110.142.83.167
  • WAN クライアントからの質問: オブジェクトと対話する方法を知りたいFoo
  • RMI レジストリ レポート: 次の URL にアクセスできますFoo192.168.0.110:<some port>
  • WAN クライアントが に接続しようとしまし192.168.0.110:<some port>たが、そのマシンに到達できません

java.rmi.server.hostnameを明示的な値 ( など) に設定できますが、すべてのクライアントがその IP アドレスを使用してリモート オブジェクトに接続しようとすることに110.142.83.167注意してください。

この問題を解決する 1 つの方法は、ホスト名と分割 DNS システムを使用することです。この場合、外部クライアントはホスト名を に110.142.83.167解決しますが、内部クライアントはホスト名を に解決します192.168.0.110。(代わりに、クライアント マシンでホスト ファイル エントリを使用することもできますが、クライアント マシンの数が多い場合は管理が面倒になります)。

また、リモート オブジェクトを特定のポートに明示的にバインドしていない場合は、エフェメラル ポートが使用されることに注意してください。したがって、ファイアウォールですべてのポートへのアクセスを許可する必要があります。リモート オブジェクトを特定のポートにバインドすることにより、そのポート (およびレジストリ ポート) のみを通過させるようにファイアウォールを構成できます。

于 2016-10-18T05:49:59.257 に答える