📜 ⬆️ ⬇️

Client-server step-by-step, from single-threaded to multi-threaded (Client-Server step by step)

The purpose of the publication is to show Java programmers to beginners all the steps of creating a multi-threaded server. For a complete understanding of this topic, the main information is contained in the comments of my code and in the messages displayed in the console for a better understanding of what exactly is happening and in what sequence.

In the beginning, the creation of an elementary client-server will be considered, in order to master the basic knowledge, on the basis of which a multi-threaded architecture will be built.

Concepts

- Streams: in order not to confuse what exactly is meant by a stream, I will use the synonym existing in professional literature - the thread, not to confuse Stream and Thread, yet more professionally expressed - the thread, speaking about Thread.
')
- Sockets (Sockets): this concept is also not unique, because at some point the server performs - client actions, and the client - server actions. Therefore, I have separated the concept of a server socket - (ServerSocket) and a socket (Socket) through which communication is practically carried out, we will call it a communication socket, so that it is clear what we are talking about.

In addition, the communication sockets are created one by one on each of the data exchanging applications, therefore the application socket that has the object - ServerSocket and initially opens the port while waiting for a connection, we call the communication socket on the server side, and the second socket that creates the port connecting to the port the application will be called the client-side communication socket.

Thanks for the hint about Thread.sleep () ;! Of course in real code Thread.sleep (); No need to install - it's a muveton! In this publication, I use it only to make the execution of the program clearer, in order to have time to understand what is happening. So test, learn and never use Thread.sleep () ;! in your code!

Table of contents:

1) Single threaded elementary server.
2) Customer.
3) Multi-threaded server - by itself, this server does not participate in communication directly, but is only a factory of single-line delegates (delegated to conduct a dialogue with server clients) to communicate with newly connected clients, which are closed after the end of communication with the client.
4) Simulation of multiple client calls to the server.

According to numerous comments, I post a link to the source code on GitHub :

So let's start by examining the structure of a single-threaded server, which can accept only one client for a dialogue. The code below needs to be run in your IDE in this idea for the whole article. I propose to understand all the details from the detailed documented code below:


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"); 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(); } } } 



The server is running and is blocking waiting for server.accept (); accessing it with a connection request. Now you can connect to the client, write the client code and run it. The client works when the user enters something in his console (note! In this case, the server and the client run on the same computer with the local address localhost, so when entering the lines that the client should send, remember to make sure that you switch to the client's working console !)
After entering the line in the client console and pressing enter, the line is checked whether the client has entered the code word to end the conversation, then it is sent to the server, where it reads it and checks the same for the presence of the exit code word. Both the client and the server receiving the code word close the resources after the preliminary preparations and complete their work.
Let's see how it looks in code:

 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.available()!=0) { 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.available()!=0) { //              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) Multi-threaded server

What if another client wants to connect to the server !? After all, the server described above is either waiting for the connection of one client, or communicating with it before the connection is completed, what should other clients do? For such a case, you need to create a factory that will create the servers described above when connecting to the socket of new clients and not waiting for the delegated subserver to finish the dialogue with the client open accept () waiting for the next client. But in order for the server machine to have enough resources to communicate with many clients, you need to limit the number of possible connections. The factory will produce a slightly modified version of the previous server (the modification will concern the fact that the server class for the factory will implement the interface - Runnable to allow its use in the thread pool - ExecuteServices). Let's create such a server factory and get acquainted with a detailed description of its work in the code:

  • Factory:

 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(); } } } 

  • Modified Runnable server to run from previous code:

 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(); } } } 

To simulate multiple client access to the server, we will create and run (after starting the server part) a factory of Runnable clients that will connect to the server and write messages in a loop:

  • 4) Simulation of multiple client calls to the server.

 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(); } } 

As can be seen from the previous code, the factory runs TestRunnableClientTester () clients, write the code for them and then run the factory itself so that it has someone to execute in its pool:

 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(); } } } 

Run, make changes to the code, the only way to really understand the work of this structure.

Thanks for attention.

Source: https://habr.com/ru/post/357688/


All Articles