BIO, that is, blocking IO. During the Socket-based message communication process, the Socket server provides services to the outside, and the Socket client can establish a connection to the Socket server, then send request data, then wait for the Socket server to process, and return the processing result (response).
Based on BIO communication, the Socket server will block, that is, every time the Socket connection to a client is accepted during the listening process, the request must be processed, and at this time, other connected clients can only block and wait. It can be seen that the processing capability of the Socket server in this mode is very limited, and the client can only wait until the server is idle and handles the request.
BIO communication implementation
The following is based on the BIO model to implement the logic of a simple Socket server communicating with the Socket client, and have a perceptual understanding of this communication method. The specific logic is described as follows:
1. The Socket client connects to the Socket server and sends the data "I am the client N.";
2. The Socket server listens to the service port and receives the client request data. If the request data starts with "I am the client", it responds to the client "I am the server, and you are the Nth client.";
The Socket server implementation is as follows:
package org.shirdrn.java.communications.bio; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; /** * Socket server-based on BIO* * @author shieldrn */ public class SimpleBioTcpServer extends Thread { /** Service port number*/ private int port = 8888; /** Assign number*/ private static int to the client sequence = 0; public SimpleBioTcpServer(int port) { this.port = port; } @Override public void run() { Socket socket = null; try { ServerSocket serverSocket = new ServerSocket(this.port); while(true) { socket = serverSocket.accept(); // Listen to this.handleMessage(socket); // HandleMessage(socket); // Handle a connected client request} } catch (IOException e) { e.printStackTrace(); } } /** * Handle a client socket connection* @param socket client socket * @throws IOException */ private void handleMessage(Socket socket) throws IOException { InputStream in = socket.getInputStream(); // Stream: Client->Server (read) OutputStream out = socket.getOutputStream(); // Stream: Server->Client (write) int receiveBytes; byte[] receiveBuffer = new byte[128]; String clientMessage = ""; if((receiveBytes=in.read(receiveBuffer))!=-1) { clientMessage = new String(receiveBuffer, 0, receiveBytes); if(clientMessage.startsWith("I am the client")) { String serverResponseWords = "I am the server, and you are the " + (++sequence) + "th client."; out.write(serverResponseWords.getBytes()); } } out.flush(); System.out.println("Server: receives clientMessage->" + clientMessage); } public static void main(String[] args) { SimpleBioTcpServer server = new SimpleBioTcpServer(1983); server.start(); } } The above implementation does not perform complex exception processing.
The Socket client implementation is as follows:
package org.shirdrn.java.communications.bio; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; import java.util.Date; /** * Socket client based on BIO* * @author shieldrn */ public class SimpleBioTcpClient { private String ipAddress; private int port; private static int pos = 0; public SimpleBioTcpClient() {} public SimpleBioTcpClient(String ipAddress, int port) { this.ipAddress = ipAddress; this.port = port; } /** * Connect to the Socket server and simulate sending request data* @param data Request data*/ public void send(byte[] data) { Socket socket = null; OutputStream out = null; InputStream in = null; try { socket = new Socket(this.ipAddress, this.port); // Connect // Send a request out = socket.getOutputStream(); out.write(data); out.flush(); // Receive response in = socket.getInputStream(); int totalBytes = 0; int receiveBytes = 0; byte[] receiveBuffer = new byte[128]; if((receiveBytes=in.read(receiveBuffer))!=-1) { totalBytes += receiveBytes; } String serverMessage = new String(receiveBuffer, 0, receiveBytes); System.out.println("Client: receives serverMessage->" + serverMessage); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { try { // Send a request and receive a response, communication is completed, close the connection out.close(); in.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } public static void main(String[] args) { int n = 1; StringBuffer data = new StringBuffer(); Date start = new Date(); for(int i=0; i<n; i++) { data.delete(0, data.length()); data.append("I am the client ").append(++pos).append("."); SimpleBioTcpClient client = new SimpleBioTcpClient("localhost", 1983); client.send(data.toString().getBytes()); } Date end = new Date(); long cost = end.getTime() - start.getTime(); System.out.println(n + " requests cost " + cost + " ms."); } } First start the Socket server process SimpleBioTcpServer, and then run the Socket client SimpleBioTcpClient. It can be seen that the server receives the request data and then responds to the client, and the client receives the response data from the server.
In the above implementation, both the Socket client and the server are written and read out at once. In reality, if the amount of data is particularly large during each communication, the server cannot accept it. It can loop to read and process it when determining the number of data bytes requested by the client.
In addition, if the above-mentioned stream is not wrapped, there will be performance losses in practice, such as the inability to buffer, etc.
For Socket server receiving data, it is much more convenient if the byte data read from multiple loops can be stored through a variable-length byte buffer. However, using ByteArrayOutputStream, for example:
ByteArrayOutputStream data = new ByteArrayOutputStream(); data.write(receiveBuffer, totalBytes, totalBytes + receiveBytes);
BIO communication test
The following tests the efficiency of Socket server processing in a scenario with a large number of requests.
The first method: start 5000 Socket clients through a for loop and send a request. The code is as follows:
public static void main(String[] args) { int n = 5000; StringBuffer data = new StringBuffer(); Date start = new Date(); for(int i=0; i<n; i++) { data.delete(0, data.length()); data.append("I am the client ").append(++pos).append("."); SimpleBioTcpClient client = new SimpleBioTcpClient("localhost", 1983); client.send(data.toString().getBytes()); } Date end = new Date(); long cost = end.getTime() - start.getTime(); System.out.println(n + " requests cost " + cost + " ms."); } After testing, it takes about 9864ms, which is about 10s.
The second method: start 5,000 independent client threads and request at the same time, and the server counts:
package org.shirdrn.java.communications.bio; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; import java.util.Date; /** * Socket communication test based on BIO* * @author shieldrn */ public class SimpleBioTcpTest { static int threadCount = 5000; /** * Socket server process based on BIO* * @author shieldrn */ static class SocketServer extends Thread { /** Service port number*/ private int port = 8888; /** Assign a number to the client*/ private static int sequence = 0; public SocketServer(int port) { this.port = port; } @Override public void run() { Socket socket = null; int counter = 0; try { ServerSocket serverSocket = new ServerSocket(this.port); boolean flag = false; Date start = null; while(true) { socket = serverSocket.accept(); // Listen // Time starts if(!flag) { start = new Date(); flag = true; } this.handleMessage(socket); // Handle a connected client request if(++counter==threadCount) { Date end = new Date(); long last = end.getTime() - start.getTime(); System.out.println(threadCount + " requests cost " + last + " ms."); } } } } catch (IOException e) { e.printStackTrace(); } } /** * Handle a client socket connection* @param socket Client socket * @throws IOException */ private void handleMessage(Socket socket) throws IOException { InputStream in = socket.getInputStream(); // Stream: Client->Server (read) OutputStream out = socket.getOutputStream(); // Stream: Server->Client (write) int receiveBytes; byte[] receiveBuffer = new byte[128]; String clientMessage = ""; if((receiveBytes=in.read(receiveBuffer))!=-1) { clientMessage = new String(receiveBuffer, 0, receiveBytes); if(clientMessage.startsWith("I am the client")) { String serverResponseWords = "I am the server, and you are the " + (++sequence) + "th client."; out.write(serverResponseWords.getBytes()); } } out.flush(); System.out.println("Server: receives clientMessage->" + clientMessage); } } /** * Socket client thread based on BIO* * @author shieldrn */ static class SocketClient implements Runnable { private String ipAddress; private int port; /** Request data to be sent*/ private String data; public SocketClient(String ipAddress, int port) { this.ipAddress = ipAddress; this.port = port; } @Override public void run() { this.send(); } /** * Connect to the Socket server and simulate sending request data*/ public void send() { Socket socket = null; OutputStream out = null; InputStream in = null; try { socket = new Socket(this.ipAddress, this.port); // Connect// Send request out = socket.getOutputStream(); out.write(data.getBytes()); out.flush(); // Receive response in = socket.getInputStream(); int totalBytes = 0; int receiveBytes = 0; byte[] receiveBuffer = new byte[128]; if((receiveBytes=in.read(receiveBuffer))!=-1) { totalBytes += receiveBytes; } String serverMessage = new String(receiveBuffer, 0, receiveBytes); System.out.println("Client: receives serverMessage->" + serverMessage); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { try { // Send a request and receive a response, communication is completed, close the connection out.close(); in.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } public void setData(String data) { this.data = data; } } public static void main(String[] args) throws Exception { SocketServer server = new SocketServer(1983); server.start(); Thread.sleep(3000); for(int i=0; i<threadCount; i++) { SocketClient client = new SocketClient("localhost", 1983); client.setData("I am the client " + (i+1) + "."); new Thread(client).start(); Thread.sleep(0, 1); } } }After testing, it takes about 7110ms, which is about 7s, and there is no big improvement.
BIO communication improvements
Through the above tests, we can find that when the Socket server processes requests from the client, blocking occurs, which seriously affects the efficiency of concurrent processing of requests. In fact, within the scope of the Socket server's connection capability, the received request can be independent, so as to solve the above problem by processing requests by one request and one thread. In this way, the server side has multiple processing threads corresponding to the client's multiple requests, and the processing efficiency has been improved to a certain extent.
Below, receive the request through a single thread, and then delegate the thread pool to perform multi-threaded concurrent processing requests:
/** * Socket server process based on BIO* * @author shieldrn */ static class SocketServer extends Thread { /** Service port number*/ private int port = 8888; /** Assign number to the client*/ private static int sequence = 0; /** Thread pool for processing client requests*/ private ExecutorService pool; public SocketServer(int port, int poolSize) { this.port = port; this.pool = Executors.newFixedThreadPool(poolSize); } @Override public void run() { Socket socket = null; int counter = 0; try { ServerSocket serverSocket = new ServerSocket(this.port); boolean flag = false; Date start = null; while(true) { socket = serverSocket.accept(); // Listen// Time starts if a request comes if (!flag) { start = new Date(); flag = true; } // Put client request into the thread pool to process pool.execute(new RequestHandler(socket)); if(++counter==threadCount) { Date end = new Date(); long last = end.getTime() - start.getTime(); System.out.println(threadCount + " requests cost " + last + " ms."); } } } catch (IOException e) { e.printStackTrace(); } } /** * Client request processing thread class* * @author shieldrn */ class RequestHandler implements Runnable { private Socket socket; public RequestHandler(Socket socket) { this.socket = socket; } @Override public void run() { try { InputStream in = socket.getInputStream(); // Stream: Client->Server (read) OutputStream out = socket.getOutputStream(); // Stream: Server->Client(write) int receiveBytes; byte[] receiveBuffer = new byte[128]; String clientMessage = ""; if((receiveBytes=in.read(receiveBuffer))!=-1) { clientMessage = new String(receiveBuffer, 0, receiveBytes); if(clientMessage.startsWith("I am the client")) { String serverResponseWords = "I am the server, and you are the " + (++sequence) + "th client."; out.write(serverResponseWords.getBytes()); } } out.flush(); System.out.println("Server: receives clientMessage->" + clientMessage); } catch (IOException e) { e.printStackTrace(); } } } } } } } } }It can be seen that this improved method enhances the concurrency of the server processing requests, but each request must be processed by a thread. A large number of requests cause the server to start a large number of processes for processing, which is also relatively occupies server resources.
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.