3

私はWindowsXPのEclipseでJavaをプログラミングしています。ProcessBuilderを使用してサーバーと2つのクライアントを実行するマルチプロセスシミュレーションがあります。サーバーはスレッドを開始して、クライアントごとに1つずつ、2つの異なるソケットをリッスンします。各クライアントのコードをコメントアウトして、他のクライアントを完全に正常に動作させることができます。両方を実行しようとすると、1つのクライアントが常にConnectExceptionでエラーになります:接続が拒否されました:接続します。どちらが遅いかはわかりませんが、どちらのクライアントであるかはわかりません。サーバーを起動した後、クライアントの前で一時停止できます。netstatは、両方のソケットがアクティブであることを確認します。これを引き起こしている可能性がありますか?以下にいくつかの簡略化されたコードがあります。

更新:コメントに基づいて、単一のソケットでサーバーをマルチスレッド化するようにコードを編集しましたが、それでも同じ問題が発生します。以下のコードは変更を反映しています。他のクライアントがソケットを開く前に、一方のクライアントがソケットを開いたり閉じたりしているようです。各クライアントの最後に一時停止ステートメントをスローして、他のクライアントを終了させることができますが、これは修正であり、解決策ではありません。では、本当の問題は、閉じるように指示するまでServerSocketをリッスンし続けるにはどうすればよいかということです。

サーバ

try{
    server = new ServerSocket(sockNum); 
} catch (IOException e) {
        System.out.printf("Could not listen on port %d\n",sockNum);
        System.exit(-1);
}
while(true){
    ClientWorker w;
    try{
        Socket connection = server.accept();
        w = new ClientWorker(connection);
        Thread t = new Thread(w);
        t.start();
    } catch (IOException e) {
        System.out.printf("Accept failed: %d\n",sockNum);
        System.exit(-1);
    }
}

class ClientWorker implements Runnable {
    private Socket client;

          ClientWorker(Socket client) {
           this.client = client;
          }

          public void run(){
            Object line;
            ObjectInputStream in = null;
            PrintWriter out = null;
            try{
              in = new ObjectInputStream(client.getInputStream());
              out = new PrintWriter(client.getOutputStream(), true);
            } catch (IOException e) {
              System.out.println("in or out failed");
              System.exit(-1);
            }

            while(true){
                try{
                    line = in.readObject();
                    //Send data back to client
                    out.println("Sent from broker");
                    if(line instanceof String)
                        System.out.println(line);
                    else
                        System.out.println(line.getClass());                        
                } catch (IOException e) {
                    System.out.println("Read failed");
                    System.exit(-1);
                } catch (ClassNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }

クライアント

 try{
    socket = new Socket("localhost", socketNum);
    out = new ObjectOutputStream(socket.getOutputStream());
    in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

    out.writeObject(message);
    String line = in.readLine();
    System.out.println(line);
    } catch (UnknownHostException e) {
        System.out.println("Unknown host: localhost.eng");
        System.exit(1);
    } catch  (IOException e) {
        System.out.println("No I/O");
        System.exit(1);
    }

コントローラ

ProcessBuilder server = new ProcessBuilder("java.exe","-Xss64m","-cp","bin;jscheme.jar","ServerProcess");
server.redirectErrorStream(true);
Process runServer = server.start();

ProcessBuilder clientA = new ProcessBuilder("java.exe","-Xss64m","-cp","bin;jscheme.jar","ClientAProcess");
clientA.redirectErrorStream(true);
Process runClientA = clientA.start();

ProcessBuilder clientB = new ProcessBuilder("java.exe","-Xss64m","-cp","bin;jscheme.jar","ClientBProcess");
clientB.redirectErrorStream(true);
Process runClientB = clientB.start();
4

4 に答える 4

1

「サーバーはスレッドを開始して、クライアントごとに1つずつ、2つの異なるソケットをリッスンします。」

いいえ、これをしないでください!クライアントごとに個別のリスニングソケットを割り当てるのは異常な設計です。両方のクライアントが接続する1つのリスニングスレッドを開始するだけです。accept()が戻ったら、そのクライアントの読み取りスレッドを開始し、accept()呼び出しによって返されたServerClientソケットを渡します。

于 2012-04-16T21:33:47.467 に答える
1

サーバーを作成する通常の方法は、ループ内の単一のソケットでリッスンすることです。

着信接続ごとに、クライアントを処理するためのスレッドを生成し、すぐlistenに同じサーバーソケットで再度実行します。

あなたの場合、2つのクライアントが同じサーバーポートに接続しようとすると、2番目のクライアントは常に「接続が拒否されます」。最初のクライアントが接続を取得し、サーバーはlistenソケットでを再発行しません。クライアントがポート番号を取得する方法を示していませんが、両方とも同じポートに接続しようとしていると思います。

于 2012-04-16T21:35:54.593 に答える
0

私がこれを書いているとき、あなたのコードは私にはよく見えます。新しい接続をリッスンするServerSocketが1つあります。各接続は、それが発生すると、それを処理するための新しいスレッドをセットアップし、それに独自のソケット与えます。ソケットとそのスレッドは完全に独立している必要がありますそれぞれの運命は、互いに完全に独立している必要があります。彼らはお互いの存在を知らないはずです。

それらがどのように相互作用しているかを把握し、それらを分離します。

試してみる1つのことは、2ではなく100のクライアントを設定することです。これにより、問題がより明白になる可能性があります。1トンのTNTは、爆竹よりも大きなクレーターを残します。

もう1つの考えられる問題:ObjectOutputStreamを定期的にリセットしていることを確認してください。この種のI/Oは非常に便利ですが、非常に奇妙なことができます。リセットせずに、同じオブジェクトを2回送信すると、2回目は最初のバージョンへの参照のみが送信されます。これは、最新バージョンが変更されている場合は役に立ちません。また、リセットを使用しても、別のオブジェクトを参照するオブジェクトを送信すると、そのオブジェクトも送信されます。(これは非常に便利です。何百万ものオブジェクトを含む広大な構造を参照する単一のオブジェクトを送信でき、すべてが送信されその組織が維持されます。それが何をしているのかを知っていれば、独創的で本当に便利です。)

于 2012-04-17T18:05:32.157 に答える
0

OPから

ですから、私が無関係だと思っていた問題に取り組む過程で、私は最初の問題を修正したようです。私が追加した主なものは2つあります。まず、完了時に「終了」コマンドを送信するようにクライアントを設定し、readObject()を試行するときにソケットがブロックされないようにしました。次に(これが修正されたと思います)、ServerSocketのタイムアウト値を未定義(別名無限)のままにするのではなく、定義しました。動作した後にコードをクリーンアップするために戻ったとき、クライアントプロセスの一時停止を削除しようとしましたが、それでも動作しました。私の推測では、タイムアウトが定義されていない場合、一方のソケットがもう一方のソケットが開かれる前に閉じられると、ServerSocketも閉じられます。タイムアウトが定義されている場合、ServerSocketはタイムアウトになるまで待機してから閉じます。(これは単なる推測です。)

これが現在の作業コードです。修正の別の原因があると思われる場合は、遠慮なくコメントしてください。

サーバ

try{
    server = new ServerSocket(sockNum);
    server.setSoTimeout(timeOut);
} catch (IOException e) {
    System.out.printf("Could not listen on port %d\n",sockNum);
    System.exit(-1);
}

while(isActive){
    ClientWorker w;
    try{
        Socket connection = server.accept();
        w = new ClientWorker(connection);
        Thread t = new Thread(w);
        t.start();
    } catch (IOException e) {
        if(e instanceof SocketTimeoutException)
            isActive = false;
        else{
            System.out.printf("Accept failed: %d\n",sockNum);
            System.exit(-1);
        }
    }
}

 class ClientWorker implements Runnable {
        private Socket client;
        private Source source = Source.NONE;
        private String sourceName = "?";
        private Boolean threadActive = true;

        ClientWorker(Socket client) {
            this.client = client;
        }

        public void run(){
            Object line;
            ObjectInputStream in = null;
            PrintWriter out = null;
            try{
                in = new ObjectInputStream(client.getInputStream());
                out = new PrintWriter(client.getOutputStream(), true);
            } catch (IOException e) {
                System.out.println("in or out failed");
                System.exit(-1);
            }

            while(threadActive){
                try{
                    line = in.readObject();
                    if(line instanceof String){
                        if(line.equals("end")){
                            threadActive = false;
                        }else
                            sourceName = line;
                        }
                    } else 
                        handleData;
                } catch (IOException e) {
                    System.out.println("Read failed");
                    threadActive = false;
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                    threadActive = false;
                }
            }
            try {
                this.client.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
于 2012-05-01T13:50:48.373 に答える