web-dev-qa-db-ja.com

Javaソケット:同じマシンの同じポートにある複数のクライアントスレッド?

私はJavaでソケットプログラミングに不慣れで、以下のコードが間違っていないかどうかを理解しようとしていました。私の質問は次のとおりです。

同じプログラムのサーバーインスタンスに接続しようとする各スレッドに複数のクライアントがあり、サーバーがクライアント間で分離してデータを読み書きすることを期待できますか?」

public class Client extends Thread
{
    ...
    void run()
    {
        Socket socket = new Socket("localhost", 1234);
        doIO(socket);  
    }
}

public class Server extends Thread
{
    ...
    void run()
    {
        // serverSocket on "localhost", 1234
        Socket clientSock = serverSocket.accept();
        executor.execute(new ClientWorker(clientSock));
    }
}

現在のマシンの同じポートに接続しようとする複数のクライアントインスタンスを異なるスレッドで使用できますか?

例えば、

   Server s = new Server("localhost", 1234);
   s.start();
   Client[] c = new Client[10];
   for (int i = 0; i < c.length; ++i)
   {
        c.start();
   }
18
espcorrupt

はい。ただし、記述されているように、スレッドの実行ごとに接続できるクライアントは1つだけです。

サーバーのrun()をwhile trueループ内に置くだけで、複数のクライアントを接続できます。 executorに応じて、それらは直列または並列で実行されます。

   public class Server extends Thread  
   {  
       ...  
       void run()  
       {  
           while(true){
              // serverSocket on "localhost", 1234  
              Socket clientSock = serverSocket.accept();  
              executor.execute(new ClientWorker(clientSock));  
           }
       }  
   } 
10
patros

リスニングのためにポートをバインドしようとしているオブジェクトが1つしかない限り、複数のクライアントが接続しても問題はありません。

3
Jherico

この例では、Serverは一度に1つのクライアント接続を受け入れて処理します。接続しようとするClientsはいくつでも持つことができますが、一度に1つだけが処理されます。

実装を提供していないため、エグゼキューターロジックがマルチスレッドであるかどうかは明らかではありません。エグゼキュータがスレッドプールなどに委任する場合、複数のインスタンスが並行して実行されるため、ClientWorkerがスレッドセーフであることを確認する必要があります。

もちろん、私はあなたのClientもスレッドセーフであることを前提としています。あなたの質問はServerのみに関するものだからです。

1
danben

はい、クライアントがローカルかリモートかは問題ではありません。この例で重要なことは、サーバーにはそのクラスの複数のインスタンス(クライアント接続ごとに1つ)があるため、ClientWorkerはスレッドセーフであるということです。

0
G__

あなたはこれらの行で何かを試すことができます

public class MultiThreadServer extends Application {
  // Text area for displaying contents
  private TextArea ta = new TextArea();

  // Number a client
  private int clientNo = 0;

  @Override // Override the start method in the Application class
  public void start(Stage primaryStage) {
    // Create a scene and place it in the stage
    Scene scene = new Scene(new ScrollPane(ta), 450, 200);
    primaryStage.setTitle("MultiThreadServer"); // Set the stage title
    primaryStage.setScene(scene); // Place the scene in the stage
    primaryStage.show(); // Display the stage

    new Thread( () -> {
      try {
        // Create a server socket
        ServerSocket serverSocket = new ServerSocket(8000);
        ta.appendText("MultiThreadServer started at " 
          + new Date() + '\n');

        while (true) {
          // Listen for a new connection request
          Socket socket = serverSocket.accept();

          // Increment clientNo
          clientNo++;

          Platform.runLater( () -> {
            // Display the client number
            ta.appendText("Starting thread for client " + clientNo +
              " at " + new Date() + '\n');

            // Find the client's Host name, and IP address
            InetAddress inetAddress = socket.getInetAddress();
            ta.appendText("Client " + clientNo + "'s Host name is "
              + inetAddress.getHostName() + "\n");
            ta.appendText("Client " + clientNo + "'s IP Address is "
              + inetAddress.getHostAddress() + "\n");
          });

          // Create and start a new thread for the connection
          new Thread(new HandleAClient(socket)).start();
        }
      }
      catch(IOException ex) {
        System.err.println(ex);
      }
    }).start();
  }

  // Define the thread class for handling new connection
  class HandleAClient implements Runnable {
    private Socket socket; // A connected socket

    /** Construct a thread */
    public HandleAClient(Socket socket) {
      this.socket = socket;
    }

    /** Run a thread */
    public void run() {
      try {
        // Create data input and output streams
        DataInputStream inputFromClient = new DataInputStream(
          socket.getInputStream());
        DataOutputStream outputToClient = new DataOutputStream(
          socket.getOutputStream());

        // Continuously serve the client
        while (true) {
          // Receive radius from the client
          double radius = inputFromClient.readDouble();

          // Compute area
          double area = radius * radius * Math.PI;

          // Send area back to the client
          outputToClient.writeDouble(area);

          Platform.runLater(() -> {
            ta.appendText("radius received from client: " +
              radius + '\n');
            ta.appendText("Area found: " + area + '\n');
          });
        }
      }
      catch(IOException e) {
        ex.printStackTrace();
      }
    }
  }

  /**
   * The main method is only needed for the IDE with limited
   * JavaFX support. Not needed for running from the command line.
   */
  public static void main(String[] args) {
    launch(args);
  }
}
0
Kashif

そう。始める:

runメソッドでは1つしか受け入れないため、1つのサーバーソケットでより多くのクライアントを受け入れることができます。もう一度accept()を呼び出すだけです。

次に、forループを使用します。最初に、新しいClientオブジェクトを作成する必要があります。その後、c[i].start();ではなくc.start()を呼び出すことができます。

現在のマシンの同じポートに接続しようとする複数のクライアントインスタンスを異なるスレッドで使用できますか?

はい、できます。新しいスレッドを作成して実行するだけです。これは完全に機能するはずです。

サーバーがクライアント間を分離してデータを読み書きすることを期待する

File-ioのような基本的なIOテクニックの経験を使用できます。

OutputStream os = socket.getOutputStream();
PrintStream pw = new PrintStream(os, true); // Or PrintWriter, I don't know what the best one is.
pw.println("Hello, other side of the connection!");

読み取りにはBufferedReaderを使用します。

0