4

私は少なくとも5時間それで立ち往生していて、ここで尋ねる以外に他の手段はありません。私はRMIアプリケーションを書いています。NoteBoardImplサーバーにリモートオブジェクト(ここ)をバインドしてもらいたいのですが、これはクライアントによって検索されます。クライアントはリスナー(NoteBoardListenerここ)をサーバーに渡します。リスナーはクライアントによってエクスポートされたリモートオブジェクトでもあります。

ここで簡単なSSCCEを用意しましたので、誰かが調べてくれることを心から願っています。すべてのクラスは、デフォルトのパッケージの同じフォルダーにあります。推奨されていないことはわかっているので、アプリケーションを3つのjarに分割する必要がありましたが、ここではできるだけシンプルにしたいと思いました。

リモートインターフェース:

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface INoteBoard extends Remote {
    public void test(INoteBoardListener listener) throws RemoteException;
}


import java.rmi.Remote;
import java.rmi.RemoteException;

public interface INoteBoardListener extends Remote {
    public void onNewText(String text) throws RemoteException;
}

インターフェイスの実装:

import java.rmi.RemoteException;

public class NoteBoardImpl implements INoteBoard {
    @Override
    public void test(INoteBoardListener listener) throws RemoteException {
        listener.onNewText("server call the listener");
    }
}


import java.rmi.RemoteException;

public class NoteBoardListener implements INoteBoardListener {
    @Override
    public void onNewText(String text) throws RemoteException {
        System.out.println(text);
    }
}

クライアントとサーバー:

import java.rmi.Naming;
import java.rmi.server.UnicastRemoteObject;

public class Client {
    public static void main(String[] args) {
        if (args.length < 2) {
            System.out.println("2 arguments required:\nRMI_IP RMI_port");
            return;
        }
        System.setProperty("java.rmi.server.hostname", args[0]);
        try {
            INoteBoard nb = (INoteBoard) Naming.lookup(String.format("rmi://%s:%s/note", args[0], args[1]));
            INoteBoardListener l = (INoteBoardListener) UnicastRemoteObject.exportObject(new NoteBoardListener(), 0);
            nb.test(l);
            l.onNewText("client invokes listener");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


import java.rmi.Naming;
import java.rmi.server.UnicastRemoteObject;

public class Server {
    public static void main(String[] args) {
        if (args.length < 2) {
            System.out.println("2 arguments required:\nRMI_IP RMI_port");
            return;
        }
        System.setProperty("java.rmi.server.hostname", args[0]);
        try {
            INoteBoard noteBoard = (INoteBoard) UnicastRemoteObject.exportObject(new NoteBoardImpl(), 0);
            Naming.rebind(String.format("rmi://%s:%s/note", args[0], args[1]), noteBoard);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

テスト目的で分散システムをシミュレートし、仮想マシンでクライアントを実行しようとしました。ホスト-VMネットワークの仕様は次のとおりです-ホストIP=192.168.56.1、VM IP=192.168.56.101。
まず、次のコマンドを使用して、クライアントとサーバーをローカルで実行しました(rmiregistry 1099事前に開始しました)。作業ディレクトリはプロジェクトのルートであり、コンパイルされたクラスはbinディレクトリにあります:
java -cp bin -Djava.rmi.server.codebase=http://student.agh.edu.pl/~grajewsk/bin/ Server 192.168.56.1 1099
java -cp bin Client 192.168.56.1 1099
そしてそれは機能しました。

次に、同じコマンドを使用してVMでクライアントプログラムを実行しました。例外は次のとおりです。

java.rmi.ConnectException: Connection refused to host: 192.168.56.1; nested exception is: 
    java.net.ConnectException: Connection refused
    at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:619)
    at sun.rmi.transport.tcp.TCPChannel.createConnection(TCPChannel.java:216)
    at sun.rmi.transport.tcp.TCPChannel.newConnection(TCPChannel.java:202)
    at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:128)
    at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:194)
    at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:148)
    at sun.proxy.$Proxy0.test(Unknown Source)
    at Client.main(Client.java:14)
Caused by: java.net.ConnectException: Connection refused
    at java.net.PlainSocketImpl.socketConnect(Native Method)
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:327)
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:193)
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:180)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:384)
    at java.net.Socket.connect(Socket.java:546)
    at java.net.Socket.connect(Socket.java:495)
    at java.net.Socket.<init>(Socket.java:392)
    at java.net.Socket.<init>(Socket.java:206)
    at sun.rmi.transport.proxy.RMIDirectSocketFactory.createSocket(RMIDirectSocketFactory.java:40)
    at sun.rmi.transport.proxy.RMIMasterSocketFactory.createSocket(RMIMasterSocketFactory.java:146)
    at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:613)
    ... 7 more

オブジェクトがサーバーのレジストリで正常にルックアップされ、クライアント側のリモートオブジェクトがエクスポートされ(これも成功します)、14行目で実行が中断され、でメソッドを呼び出そうとしていることに注目してください。クライアント側オブジェクトを渡すサーバー側オブジェクト。

どちらのシステムにもファイアウォールがありません。両方向のpingは問題なく実行されます。私はここにいくつかの概念的な問題があるに違いないことを知っています、そして確かに私はRMIについて何かを誤解しました。よろしくお願いします。

バイナリコードベースは、私の学生のサーバーソースコードにあります。前もって感謝します!

4

2 に答える 2

4

これが私がこれの底に到達するために何をするかです。

  1. サーバーアプリを実行する
  2. RMIに使用しているポートを確認します(これは一時的なものであるため、サーバーで作成するプロセスごとに変更する必要があります)。

    • ps-efでPIDを見つける
    • 次にnetstat-anp| grepこれにより、ポート番号が表示されます。そしてそれは0.0.0.0にバインドする必要があります
  3. クライアントVMで、ncまたはtelnetを使用して、ポートに接続できることを確認します。これが失敗した場合は、ファイアウォール/iptablesに問題がある可能性があります。

  4. wiresharkを使用して、クライアントがステップ2で学習したポート/IPコンボに実際に接続しようとしていることを確認します。

pingを実行できるからといって、特定のポートに接続できるとは限らないことを忘れないでください。「iptables-L」も確認してください。

また、ネーミングは、URLのスキームコンポーネントを配置しないように言っています。形式は//host:port / nameである必要があるため、これも確認する必要があります。

于 2013-03-26T23:24:19.467 に答える
3

わかりました、最終的にそれを解決しました。java.rmi.server.hostname問題は、両方のプログラムで同じ引数を渡していたことでした。つまり、クライアントとサーバーの両方がここでサーバーのアドレスを取得したということです。クライアントが独自のオブジェクトをエクスポートする場合は、独自のIPをに提供する必要があることが判明しましたjava.rmi.server.hostname。このようにして、すべてが正常に機能します。

したがって、要約すると、サーバーに3つの引数を指定する必要がありました
。RMI_IPRMI_port server_hostname

そして、クライアントへの3つの類似した引数:
RMI_IP RMI_port client_hostname

于 2013-03-27T13:59:52.383 に答える