1

多くのクライアントが接続するRMIサーバーがあります。クライアントが変更を登録すると、サーバーが反応し、すべてのクライアントに変更を行うように指示します。

私はいくつかのhelloworldRMIの例を調べてきましたが、サーバーからクライアントへの接続を維持する方法を扱っているものはありません。

私が達成したいのは、これらの線に沿ったものです。サーバーはrmiregistryに登録されています。クライアント1はサーバーに接続し、サーバー上のメソッドを呼び出します。クライアント2はサーバーに接続し、サーバー上のメソッドを呼び出します。サーバーは変更をclient2からclient1に送信します。

すべてのクライアントをサーバーとして登録せずに、これをどのように達成しますか?

編集:いくつかの優れた回答(よりフラットでよりフラット)を見た後、次の例外に遭遇していますjava.rmi.StubNotFoundException:スタブクラスが見つかりません:thegame.connectivity.GameClient_Stub; ネストされた例外は次のとおりです。

java.lang.ClassNotFoundException: thegame.connectivity.GameClient_Stub
    at sun.rmi.server.Util.createStub(Util.java:292)
    at sun.rmi.server.Util.createProxy(Util.java:140)
    at sun.rmi.server.UnicastServerRef.exportObject(UnicastServerRef.java:196)
    at java.rmi.server.UnicastRemoteObject.exportObject(UnicastRemoteObject.java:310)
    at java.rmi.server.UnicastRemoteObject.exportObject(UnicastRemoteObject.java:237)
    at thegame.connectivity.GameClient.bind(GameClient.java:37)
    at thegame.connectivity.GameClient.run(GameClient.java:51)
    at thegame.connectivity.GameClient.main(GameClient.java:29)
Caused by: java.lang.ClassNotFoundException: thegame.connectivity.GameClient_Stub
    at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:264)
    at sun.rmi.server.Util.createStub(Util.java:286)
    ... 7 more

このクラスで

public class GameClient extends Thread implements Remote, Client, ModelChangeListener<Client>{
    private static final long serialVersionUID = -394039736555035873L;
    protected Queue<GameModelEvent> queue = new ConcurrentLinkedQueue<GameModelEvent>(); 


    public GameClient(){

    }

    public static void main(String[] args){
        GameClient client = new GameClient();
        client.run();
    }


    protected void bind(){
        System.setProperty("java.rmi.server.codebase","file:bin/");
        try {
            Registry registry = LocateRegistry.getRegistry();
            Client c = (Client)UnicastRemoteObject.exportObject(this);
            Server stub = (Server) registry.lookup("Server");
            stub.registerClient(c);
        } catch (RemoteException | NotBoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }



    @Override
    public void run() {
        super.run();
        bind();
        while(!Thread.interrupted()){
            System.out.print(".");
            GameModelEvent event = queue.poll();
            while(event != null){

                System.out.println(event);


                event = queue.poll();
            }

            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                return;
            }
        }

    }
...

UnicatRemoteObjectをコメントアウトし、パラメーターとしてnullを渡すとうまく機能します。したがって、接続があり、すべてが実行されています。しかし、スタブはありません...コードベースをコメントアウトしても効果はありません。コードベースはサーバー上で正常に機能します。

何が悪いのかについて何か考えはありますか?

4

4 に答える 4

2

クライアントが最初にRMIサーバーに接続するとき、クライアントはインターフェースを実装するある種のリスナーを提供できRemoteます。サーバーはこれらのセットを維持できRemoteListener、クライアントに何らかのイベントが通知される必要があるときはいつでも、remoteAction(ActionEvent e)作成したいものを呼び出すことができます。問題のドメインについて詳しく知らなければ、具体的にするのは難しいです。

于 2013-01-21T13:15:13.713 に答える
1

クライアントはサーバーと通信できるだけでなく、相互に直接通信することもできます。

クライアントをクライアントに登録し、少数の「マスタークライアント」から始まるツリー構造のようなものを構築します。これらのみがサーバーに登録されます。メッセージを他のサブクライアントにカスケードできるため、サーバーに直接登録されているクライアントがごくわずかで、非常に多くのクライアントがサポートされる可能性があります。

残念ながら、このアプローチでは、クライアントが適切にシャットダウンできるようにする必要があります。これにより、クライアントは、現在持っているサブクライアントに関する職務を他のノードに分散したり、サーバーに戻ったりできます。木の枝全体が切断されるため、単純に殺すことはできません。クライアントがマスターとの接続がなくなったと感じた場合は、サーバーにアピールして別のサーバーを取得する必要があります。

実行できるドキュメントも参照してください。

また、使用するにはスタブクラスが必要であるため、呼び出さないでくださいexportObject(Remote obj)(クラスのインスタンスを返しますRemoteStub)。exportObject(Remote obj, int port)インターフェイスを返す使用。ここでもカバーされています。

于 2013-01-21T13:24:58.360 に答える
1

これは、RMIコールバックを使用して実現できます。詳細とソースコードの多くは、http://docs.oracle.com/cd/E13211_01/wle/rmi/callbak.htm#1023775で入手できます。

于 2013-01-24T12:53:33.120 に答える
0

回答パートI)

純粋なRMIでこれを行うには、クライアントはコールバックの目的でRMIサーバーとして機能する必要があります。それから逃れることはできません。RMIプロトコルがどのように機能するかです。

各クライアントをこのように動作させたくない場合は、ネットワークスタックのレベルを下げて、RMIの代わりに独自のソケットプロトコルを実装する必要があります。サーバーが登録メッセージを受信すると、必要に応じて、クライアントがそのソケットを介してデータをクライアントにプッシュバックすることによって確立されたソケット接続を保持する必要があります。

回答パートII(質問の編集後))

UnicastRemoteObject.exportObject(Remote)JDKソースからの次のコメントに示されているように、使用時にクライアントをエクスポートするときは、スタブを事前に生成しておく必要があります。

生成されたスタブクラスのみを使用する必要があることを示すには、ブール値trueを渡すUnicastServerRefコンストラクターを使用します。このメソッドの戻り値は、動的プロキシクラスが拡張できないRemoteStubであるため、動的プロキシの代わりに生成されたスタブクラスを使用する必要があります。

したがって、java.lang.ClassNotFoundException

まあ、それか電話してUnicastRemoteObject.exportObject(Remote, int)ください。

また、呼び出しでサービス名を指定して、スタブをレジストリにバインドする必要がありますRegistry.bind(String, Remote)

これは事実上、クライアントをRMIサーバーとして実行しています-私の回答「回答パートI」で述べたように、これはRMIを使用するときにこれが機能する唯一の方法です-RMIはそこでリモートメソッド呼び出しを行うだけです。

于 2013-01-21T13:18:04.910 に答える