Client-server step — by — step, from single-threaded to multi-threaded (Client-Server step by step)
the Aim of this paper is to show novice Java programmers through the steps of creating a multithreaded server. For a full understanding of the topic basic information contained in the comments of my code and the output in the console messages for a better understanding of what is happening and in what sequence.
At the beginning we will create a basic client-server for learning basic knowledge upon which to build multi-threaded architecture.
Concepts.
Streams: in order not to confuse what is meant by the thread I will use existing in the professional literature as a synonym for the thread not to confuse the Stream and the Thread is still more professional to speak to the thread talking about the Thread.
— Sockets(Sockets): this concept is also not clear because at some point the server performs client actions and client — server. So I divided the notion of a server socket (ServerSocket) and socket (Socket) through which practically carried out communication, it will call the socket communication, so it was clear what it was about.
the
Besides the socket communication is created, one for each communicating application, so a socket application which has the object and the original ServerSocket opens a port to connect to socket communication on server side and socket which creates the connecting to the port known address the second application will be called by the socket communication on the client side.
Thanks for the tip about the Thread.sleep();!
Of course in real code Thread.sleep(); is not necessary to install this moveton! In this publication I use it only to execute programs make it clearer, what would have time to understand what is happening.
So test, learn and in your code, never use Thread.sleep();!
contents:
1) basic single-threaded server.
2) the Client.
3) multi-threaded server – this server does not participate in communication directly, and are only factory admonitive delegates(the delegate for dialogue with clients servers) to communicate with newly connected clients, which are closed after the communication with the client.
4) Simulation of multiple clients to the server.
According to numerous observations, post a link to the source code on GitHub:
(https://github.com/merceneryinbox/Clietn-Server_Step-by-step.git)
let's start with the study of the structure of a single-threaded server that can accept only one client for dialogue. The code below must be run in your IDE, that's the idea of the whole article. Offer all the details to understand the detail of the documented code below:
the
1) basic single-threaded server.
the
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 {
// start server on port 3345
try (ServerSocket server= new ServerSocket(3345)){
// be waiting for a socket connection under the name "client" on the server side
Socket client = server.accept();
// after handshaking the server is connecting client with this socket connection
System.out.print("Connection accepted.");
// initiate channels of communication in the socket for the server
// write channel to the socket
DataOutputStream out = new DataOutputStream(client.getOutputStream());
System.out.println("created DataOutputStream");
// channel read from the socket
DataInputStream in = new DataInputStream(client.getInputStream());
System.out.println("created DataInputStream");
// start a dialogue with a connected client in a loop until the socket is closed
while(!client.isClosed()){
System.out.println("Server reading from channel");
// the server waits for the channel read (inputstream) to obtain customer data
String entry = in.the readUTF();
// after receiving the data reads them
// and prints to the console
System.out.println("Server try writing to channel");
// initialize check condition of continuing to work with the client on this socket according to the code word - quit
if(entry.equalsIgnoreCase("quit")){
System.out.println("Client suicide initialize connections ...");
out.writeUTF("Server reply - "+entry + "- OK");
out.flush();
Thread.sleep(3000);
break;
}
// if the ending condition is not true work - we work - we send the echo reply back to the client
out.writeUTF("Server reply - "+entry + "- OK");
System.out.println("Server Wrote the message to client.");
// free the buffer network messages (the default message is not immediately sent to the network, and is first stored in a special message buffer, the size of which is determined by the specific settings in the system and the method flush() sends the message without waiting for the filling of the buffer according to the system settings
out.flush();
}
// if the exit condition is true turn off connection
System.out.println("Client disconnected");
System.out.println("Closing connections & channels.");
// close the first socket channels !
in.close();
out.close();
// then close the socket communication at server side!
client.close();
// then close the socket server that creates a socket of communication
// even in multi-threaded application it is not necessary to close
// to be able to set the server socket back to waiting for new connection
System.out.println("Closing connections & channels - DONE.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
the
-
the
- 2) Client.
the Server is running and is blocking waiting for the server.accept(); accessed by the connection request. You can now connect to the client, write the client code and run it. The client works when the user types something in his console (warning! in this case, the server and client run on the same computer with a local address is localhost, so if you enter strings that should be sent by the client don't forget to make sure that you switch to a working console client!).
After you enter in the client console and pressing enter the string is not checked whether the entered client code for the end of the next communication is sent to the server where it reads it and the same checks for code word output. Both the client and the server having received a code word close resources after the preliminary preparations and finish work.
Let's see how it looks in the code:
the
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.An InputStreamReader is;
import java.net.Socket;
import java.net.UnknownHostException;
public class TestASClient {
/**
*
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
// start the connection socket according to the known coordinates and initialiseren messages from the client console
try(Socket socket = new Socket("localhost", 3345);
BufferedReader br =new BufferedReader(new an 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.");
// check if the channel is live and working live if
while(!socket.isOutputShutdown()){
// waiting for client console on the subject of occurrence data
if(br.ready()){
// data is available - work
System.out.println("Client start writing in channel...");
Thread.sleep(1000);
String clientCommand = br.readLine();
// write the data from the console to a socket channel for the server
oos.writeUTF(clientCommand);
oos.flush();
System.out.println("sent message to the Clien" + clientCommand + " to server.");
Thread.sleep(1000);
// wait so the server has time to read a message from a socket and reply
// check the exit condition from the connection
if(clientCommand.equalsIgnoreCase("quit")){
// if the exit condition is reached is disconnected
System.out.println("kill Client connections");
Thread.sleep(2000);
// look what we said, the server lastly, before closing resources
System.out.println("reading...");
String in = ois.the readUTF();
System.out.println(in);
}
// after the preliminary preparations out of the write cycle a read
break;
}
// if the condition of separation is not achieved continue to work
System.out.println("Client sent message & start waiting for data from server...");
Thread.sleep(2000);
// check that we will answer the server in the message(for giving him time in the interval he had time to answer)
if(ois.read() > -1) {
// if you have time pick up the response from the channel server socket and store it in a variable ois printed on their client console
System.out.println("reading...");
String in = ois.the readUTF();
System.out.println(in);
}
}
}
// output of the communication cycle close their resources
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();
}
}
}
the
3) multi-threaded server
what if the server wants to connect to another client!? As described above, the server is either waiting for the connection of one client, or communicate with him until the completion of the connection, what do the rest of the customers? For such a case you need to create a factory which will create the above-described servers with the socket connection of new customers and not waiting until delegated ptserver finish the dialogue with the client will open the accept() waiting for the next customer. But on the server machine has enough resources to communication with a variety of customers it is necessary to limit the number of possible connections. Factory will issue a slightly modified version of the previous server(the modification will concern the fact that the server class for the factory to implement the interface Runnable to the possibility of its use in the thread pool — ExecuteServices). Let's create a server factory and a look at the detailed description of her work in the code:
the
Factory:
the
import java.io.BufferedReader;
import java.io.IOException;
import java.io.An InputStreamReader is;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author mercenery
*
*/
public class MultiThreadServer {
executeIt static ExecutorService = Executors.newFixedThreadPool(2);
/**
* @param args
*/
public static void main(String[] args) {
// start server on port 3345 and initialize a variable to handle console commands from the server
try (ServerSocket server = new ServerSocket(3345);
BufferedReader br = new BufferedReader(new an InputStreamReader(System.in))) {
System.out.println("Server socket created, command console reader for listen to server commands");
// start the loop when the condition that the server socket is not closed
while (!server.isClosed()) {
// check the incoming command from the server console if such
// was
if (br.ready()) {
System.out.println("Main Server found messages in any channel, let's look at them.");
// if command is quit then initialize the closure of the server and
// exit the loop razdachi threads monophotonic servers
String serverCommand = br.readLine();
if (serverCommand.equalsIgnoreCase("quit")) {
System.out.println("Main Server initiate exiting...");
server.close();
break;
}
}
// if the command from the server, not in the expectation
// connect to socket communication under the name "clientDialog"
// server side
Socket client = server.accept();
// after receiving a connection request the server creates a socket
// for communication with the client and sends it to a separate thread
// in the Runnable(if necessary, you can create Callable)
// monophoton thread = server - MonoThreadClientHandler and the
// continues to communicate on behalf of the server
executeIt.execute(new MonoThreadClientHandler(client));
System.out.print("Connection accepted.");
}
// close the thread pool after the completion of all threads
executeIt.shutdown();
} catch (IOException e) {
e.printStackTrace();
}
}
}
the
-
the
- Modified Runnable server to run the preceding code: the
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 {
// initiate the communication to a socket server
// write channel to the socket must be initialized first read channel to avoid blocking the program execution pending the header to the socket
DataOutputStream out = new DataOutputStream(clientDialog.getOutputStream());
// channel read from the socket
DataInputStream in = new DataInputStream(clientDialog.getInputStream());
System.out.println("created DataInputStream");
System.out.println("created DataOutputStream");
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// the main working part of //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// start a dialogue with a connected client in a loop until the socket is not
// closed by the client
while (!clientDialog.isClosed()) {
System.out.println("Server reading from channel");
// server thread is waiting in a read channel (inputstream) receiving
// customer data after receiving the data reads them
String entry = in.the readUTF();
// and prints to the console
System.out.println("READ from clientDialog message -" + entry);
// initialize check condition of continuing to work with the client
// for this socket, the code word quit in any case
if (entry.equalsIgnoreCase("quit")) {
// if the code word received is initialized to a closure
// server thread
System.out.println("Client suicide initialize connections ...");
out.writeUTF("Server reply -" + entry + "- OK");
Thread.sleep(3000);
break;
}
// if the condition is finishing work right - continue work -
// send echo back to the client
System.out.println("Server try writing to channel");
out.writeUTF("Server reply -" + entry + "- OK");
System.out.println("Server Wrote the message to clientDialog.");
// free the buffer network messages
out.flush();
// back in Nacala to read a new message
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// the main working part of //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// if the exit condition is true turn off connection
System.out.println("Client disconnected");
System.out.println("Closing connections & channels.");
// close the first socket channels !
in.close();
out.close();
// then close the socket communication with the client thread monoserver
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 requests to the server, create and start (after starting the server part) factory Runnable clients that will connect to the server and write messages in a loop:
the
-
the
- 4) Simulated multiple clients access to the server.
the
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 {
// start a pool of threads in which the number of possible threads is limited
// 10.
ExecutorService exec = Executors.newFixedThreadPool(10);
int j = 0;
// start cycle in which a pause of 10 milliseconds starting Runnable
// clients
// write some number of posts
while (j < 10) {
j++;
exec.execute(new TestRunnableClientTester());
Thread.sleep(10);
}
// close the factory
exec.shutdown();
}
}
As you can see from the previous code factory launches — TestRunnableClientTester() clients will write code and then run the factory itself, so she had someone to perform in your pool:
the
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 {
// create a socket communication on the client side in the object's constructor
System.out.println("Client connected to socket");
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
try (
// create object to write strings into scoket for
// read lines from the socket
// in try-with-resources style
DataOutputStream oos = new DataOutputStream(socket.getOutputStream());
DataInputStream ois = new DataInputStream(socket.getInputStream())) {
System.out.println("Client oos & ois initialized");
int i = 0;
// create the working cycle
while (i < 5) {
// write the message autogenerated cycle of the client in channel
// socket to server
oos.writeUTF("clientCommand" + i);
// get the message from the buffer network messages to the channel
oos.flush();
// wait so the server has time to read a message from a socket and
// reply
Thread.sleep(10);
System.out.println("Client wrote & start waiting for data from server...");
// pick up the response from the channel server socket
// client and store it in a variable ois printed on
// console
System.out.println("reading...");
String in = ois.the 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 it, make changes to code, the only way really to understand the operation of this structure.
Thank you.
Комментарии
Отправить комментарий