この出版物の目的は、初心者向けのJavaプログラマーに、マルチスレッドサーバーを作成するすべての段階を示すことです。 このトピックを完全に理解するために、主な情報はコードのコメントとコンソールに表示されるメッセージに含まれており、何がどのような順序で起こっているのかを正確に理解できます。
最初に、マルチスレッドアーキテクチャの構築に基づいて基本的な知識を習得するための基本的なクライアントサーバーの作成を検討します。
コンセプト。
-スレッド:スレッドの意味を正確に混同しないように、専門文献に存在する同義語-スレッドを使用します。
-ソケット(ソケット):この概念も明確ではありません。これは、ある時点でサーバーが実行する-クライアントアクション、およびクライアント-サーバー。 そのため、サーバーソケットの概念-(ServerSocket)と通信が実際に実行されるソケット(Socket)の概念を分け、意味を明確にするために通信ソケットと呼びます。
, - ServerSocket , .
Thread.sleep()に関する助けてくれてありがとう!
もちろん、実際のコードではThread.sleep(); インストールする必要はありません-これは悪いマナーです! この出版物では、プログラムの実行がより見えるようにするためだけに使用し、何が起こっているのかを理解できるようにします。
したがって、コードでThread.sleep();!をテスト、研究、使用しないでください。
目次:
1)シングルスレッドの基本サーバー。
2)顧客。
3)マルチスレッドサーバー-このサーバー自体は直接通信に参加しませんが、クライアントとの通信の終了後に閉じられる、新しく接続されたクライアントと通信するための(サーバークライアントとの対話用に委任された)一本鎖デリゲートのファクトリです。
4)サーバーにアクセスする複数のクライアントのシミュレーション。
多数のコメントによると、GitHubのソースへのリンクを投稿しています。
( https://github.com/merceneryinbox/Clietn-Server_Step-by-step.git )
それでは、対話用に1つのクライアントのみを受け入れることができるシングルスレッドサーバーの構造を検討することから始めましょう。 以下のコードは、この記事全体の考え方でIDEで実行する必要があります。 以下の詳細なドキュメントコードからすべての詳細を明確にすることをお勧めします。
- 1)シングルスレッドの基本サーバー。
import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class TestAsServer { /** * * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { // 3345 try (ServerSocket server= new ServerSocket(3345)){ // - "client" Socket client = server.accept(); // - System.out.print("Connection accepted."); // , // DataOutputStream out = new DataOutputStream(client.getOutputStream()); System.out.println("DataOutputStream created"); // DataInputStream in = new DataInputStream(client.getInputStream()); System.out.println("DataInputStream created"); // , while(!client.isClosed()){ System.out.println("Server reading from channel"); // (inputstream) String entry = in.readUTF(); // System.out.println("READ from client message - "+entry); // System.out.println("Server try writing to channel"); // - quit if(entry.equalsIgnoreCase("quit")){ System.out.println("Client initialize connections suicide ..."); out.writeUTF("Server reply - "+entry + " - OK"); out.flush(); Thread.sleep(3000); break; } // - - - out.writeUTF("Server reply - "+entry + " - OK"); System.out.println("Server Wrote message to client."); // ( , , , - flush() out.flush(); } // - System.out.println("Client disconnected"); System.out.println("Closing connections & channels."); // ! in.close(); out.close(); // ! client.close(); // // // System.out.println("Closing connections & channels - DONE."); } catch (IOException e) { e.printStackTrace(); } } }
- 2)顧客。
サーバーは実行中で、server.accept()のブロッキング待機中です。 接続要求で彼に連絡します。 これで、クライアントに接続し、クライアントコードを記述して実行できます。 クライアントは、ユーザーがコンソールに何かを入力すると動作します(注意!この場合、サーバーとクライアントはローカルアドレス-localhostを使用して同じコンピューターで起動されるため、クライアントが送信する行を入力するときは、クライアントの作業コンソールに切り替えることを忘れないでください!)。
クライアントのコンソールに行を入力してEnterキーを押すと、その行をチェックして、クライアントが会話を終了するためのコードワードを入力したかどうかを確認し、サーバーに送信してそこで読み取り、終了コードワードを確認します。 コードワードを受け取ったクライアントとサーバーの両方が、予備的な準備の後にリソースを閉じて、作業を完了します。
コードでどのように見えるか見てみましょう:
import java.io.BufferedReader; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; import java.net.UnknownHostException; public class TestASClient { /** * * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { // try(Socket socket = new Socket("localhost", 3345); BufferedReader br =new BufferedReader(new InputStreamReader(System.in)); DataOutputStream oos = new DataOutputStream(socket.getOutputStream()); DataInputStream ois = new DataInputStream(socket.getInputStream()); ) { System.out.println("Client connected to socket."); System.out.println(); System.out.println("Client writing channel = oos & reading channel = ois initialized."); // while(!socket.isOutputShutdown()){ // if(br.ready()){ // - System.out.println("Client start writing in channel..."); Thread.sleep(1000); String clientCommand = br.readLine(); // oos.writeUTF(clientCommand); oos.flush(); System.out.println("Clien sent message " + clientCommand + " to server."); Thread.sleep(1000); // // if(clientCommand.equalsIgnoreCase("quit")){ // System.out.println("Client kill connections"); Thread.sleep(2000); // if(ois.read() > -1) { System.out.println("reading..."); String in = ois.readUTF(); System.out.println(in); } // break; } // System.out.println("Client sent message & start waiting for data from server..."); Thread.sleep(2000); // , ( ) if(ois.read() > -1) { // ois , System.out.println("reading..."); String in = ois.readUTF(); System.out.println(in); } } } // System.out.println("Closing connections & channels on clentSide - DONE."); } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
- 3)マルチスレッドサーバー
しかし、別のクライアントがサーバーに接続したい場合はどうでしょう!? 結局のところ、上記のサーバーは1つのクライアントが接続するのを待っているか、接続が完了するまで通信している場合、他のクライアントは何をすべきでしょうか? この場合、新しいクライアントがソケットに接続されたときに上記のサーバーを作成するファクトリーを作成する必要があります。委任されたサーバーがクライアントとの対話を完了するのを待たずに、次のクライアントを見越してaccept()を開きます。 ただし、サーバーマシンが多くのクライアントと通信するのに十分なリソースを持つためには、可能な接続の数を制限する必要があります。 ファクトリは、以前のサーバーをわずかに変更したバージョンを生成します(変更は、ファクトリのサーバークラスがRunnableインターフェイスを実装して、スレッドプール-ExecutorServicesで使用できるようにすることに関係します)。 このようなサーバーファクトリを作成して、コードでその操作の詳細な説明を理解しましょう。
- 工場:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @author mercenery * */ public class MultiThreadServer { static ExecutorService executeIt = Executors.newFixedThreadPool(2); /** * @param args */ public static void main(String[] args) { // 3345 try (ServerSocket server = new ServerSocket(3345); BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) { System.out.println("Server socket created, command console reader for listen to server commands"); // while (!server.isClosed()) { // // if (br.ready()) { System.out.println("Main Server found any messages in channel, let's look at them."); // - quit // String serverCommand = br.readLine(); if (serverCommand.equalsIgnoreCase("quit")) { System.out.println("Main Server initiate exiting..."); server.close(); break; } } // // - "clientDialog" // Socket client = server.accept(); // // // Runnable( Callable) // = - MonoThreadClientHandler // executeIt.execute(new MonoThreadClientHandler(client)); System.out.print("Connection accepted."); } // executeIt.shutdown(); } catch (IOException e) { e.printStackTrace(); } } }
- 前のコードから実行するようにRunnableサーバーを変更:
import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; public class MonoThreadClientHandler implements Runnable { private static Socket clientDialog; public MonoThreadClientHandler(Socket client) { MonoThreadClientHandler.clientDialog = client; } @Override public void run() { try { // , // DataOutputStream out = new DataOutputStream(clientDialog.getOutputStream()); // DataInputStream in = new DataInputStream(clientDialog.getInputStream()); System.out.println("DataInputStream created"); System.out.println("DataOutputStream created"); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // , // while (!clientDialog.isClosed()) { System.out.println("Server reading from channel"); // (inputstream) // String entry = in.readUTF(); // System.out.println("READ from clientDialog message - " + entry); // // - quit if (entry.equalsIgnoreCase("quit")) { // // System.out.println("Client initialize connections suicide ..."); out.writeUTF("Server reply - " + entry + " - OK"); Thread.sleep(3000); break; } // - - // System.out.println("Server try writing to channel"); out.writeUTF("Server reply - " + entry + " - OK"); System.out.println("Server Wrote message to clientDialog."); // out.flush(); // } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // - System.out.println("Client disconnected"); System.out.println("Closing connections & channels."); // ! in.close(); out.close(); // clientDialog.close(); System.out.println("Closing connections & channels - DONE."); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
サーバーへの複数のクライアントアクセスをシミュレートするには、サーバーに接続してメッセージをループで書き込むクライアントのRunnableファクトリーを作成して実行します(サーバー側の起動後)。
- 4)サーバーにアクセスする複数のクライアントのシミュレーション。
import java.io.IOException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Main { // private static ServerSocket server; public static void main(String[] args) throws IOException, InterruptedException { // - // 10-. ExecutorService exec = Executors.newFixedThreadPool(10); int j = 0; // 10 Runnable // , // - while (j < 10) { j++; exec.execute(new TestRunnableClientTester()); Thread.sleep(10); } // exec.shutdown(); } }
前のコードからわかるように、ファクトリーはTestRunnableClientTester()クライアントを起動し、それらのコードを記述した後、ファクトリーを起動して、プールで実行する誰かを確保します。
import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; public class TestRunnableClientTester implements Runnable { static Socket socket; public TestRunnableClientTester() { try { // socket = new Socket("localhost", 3345); System.out.println("Client connected to socket"); Thread.sleep(2000); } catch (Exception e) { e.printStackTrace(); } } @Override public void run() { try ( // , // // try-with-resources DataOutputStream oos = new DataOutputStream(socket.getOutputStream()); DataInputStream ois = new DataInputStream(socket.getInputStream())) { System.out.println("Client oos & ois initialized"); int i = 0; // while (i < 5) { // // oos.writeUTF("clientCommand " + i); // oos.flush(); // // Thread.sleep(10); System.out.println("Client wrote & start waiting for data from server..."); // // ois , // System.out.println("reading..."); String in = ois.readUTF(); System.out.println(in); i++; Thread.sleep(5000); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
実行し、コードを変更します。これは、この構造の動作を実際に理解する唯一の方法です。
ご清聴ありがとうございました。