私は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();
}
はい。ただし、記述されているように、スレッドの実行ごとに接続できるクライアントは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));
}
}
}
リスニングのためにポートをバインドしようとしているオブジェクトが1つしかない限り、複数のクライアントが接続しても問題はありません。
この例では、Server
は一度に1つのクライアント接続を受け入れて処理します。接続しようとするClient
sはいくつでも持つことができますが、一度に1つだけが処理されます。
実装を提供していないため、エグゼキューターロジックがマルチスレッドであるかどうかは明らかではありません。エグゼキュータがスレッドプールなどに委任する場合、複数のインスタンスが並行して実行されるため、ClientWorker
がスレッドセーフであることを確認する必要があります。
もちろん、私はあなたのClient
もスレッドセーフであることを前提としています。あなたの質問はServer
のみに関するものだからです。
はい、クライアントがローカルかリモートかは問題ではありません。この例で重要なことは、サーバーにはそのクラスの複数のインスタンス(クライアント接続ごとに1つ)があるため、ClientWorkerはスレッドセーフであるということです。
あなたはこれらの行で何かを試すことができます
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);
}
}
そう。始める:
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を使用します。