、 、 BIO
1. Mechanism
The server using the BIO communication model usually has an independent Acceptor thread responsible for monitoring the connection of the client. After it receives the connection request from the client, it creates a new thread for each client request to process the link. After the processing is completed, it outputs the The stream returns the response to the client and the thread is destroyed, which is a typical request-response communication model.
2. Analysis
When the concurrent access of the client increases, the number of threads on the server and the concurrent access of the client increase in a proportional relationship of 1:1. After the threads are expanded, the performance of the system will drop sharply, and even thread stack overflow, creation failure, and eventually Downtime or dying.
Of course, we can use the thread pool on the server side to protect our system from the impact of high concurrency, but even if the thread pool is large, it is limited, so there will be a lot of requests waiting for the resources of the thread pool. Delay and concurrency will still face a very bad situation.
2. NIO
1. Mechanism
Not to mention the concept composition, there are many online.
The server using the nio communication model usually manages one or more channels by an independent thread selector (selector). When the channel registers the selector, the selector will listen to various events of the channel, such as SelectionKey.OP_ACCEPT-receive events , when the registered event occurs, the selected event - SelectionKey is obtained through the iterator. If the SelectionKey is the request connection event, the client's SocketChannel is saved and non-blocking is set, and then the readable listening event is added, so that before the data can be read, the selector You can do some other things; if the SelectionKey is a readable event, you can use the SocketChannel to obtain data through the thread pool, perform the next logical processing, and finally return the response to the client.
2. Analysis
Since the selector can judge the receiving state of the data, it can save the time of waiting for io data, and the time of monitoring the state will be very fast, which can be completed by a single thread, which also avoids the context switching of the thread.
3. Code
1. Server
public class NIOServer { //Selector private Selector selector; /** * Do some initialization work for the channel */ public void initServer(int port) throws IOException { // Get the ServerSocket channel ServerSocketChannel serverChannel = ServerSocketChannel.open(); // set the channel to non-blocking serverChannel.configureBlocking(false); // bind to port serverChannel.socket().bind(new InetSocketAddress(port)); // get selector this.selector = Selector.open(); //Register the SelectionKey.OP_ACCEPT event for this channel serverChannel.register(selector, SelectionKey.OP_ACCEPT); } /** * Use polling to monitor whether there are events that need to be processed on the selector, and if so, process them */ @SuppressWarnings("unchecked") public void listen() throws IOException { // polling access selector while (true) { //When the registered event arrives, the method returns; otherwise, the method will always block selector.select(); // Get the iterator of the selected item in the selector, the selected item is the registered event Iterator ite = this.selector.selectedKeys().iterator(); while (ite.hasNext()) { SelectionKey key = (SelectionKey) ite.next(); // delete the selected key to prevent repeated processing ite.remove(); // client request connection event if (key.isAcceptable()) { ServerSocketChannel server = (ServerSocketChannel) key .channel(); // Get the channel to connect to the client SocketChannel channel = server.accept(); // set to non-blocking channel.configureBlocking(false); // can send information to the client channel.write(ByteBuffer.wrap(new String("abc").getBytes())); //After the connection with the client is successful, in order to receive the client's information, the read permission needs to be set for the channel. channel.register(this.selector, SelectionKey.OP_READ); } else if (key.isReadable()) { // get readable events read(key); } } } } /** * Processing logic */ public void read(SelectionKey key) throws IOException{ // The server can read the message: get the Socket channel where the event occurred SocketChannel channel = (SocketChannel) key.channel(); // create read buffer ByteBuffer buffer = ByteBuffer.allocate(10); channel.read(buffer); byte[] data = buffer.array(); String msg = new String(data).trim(); System.out.println("Server received message: "+msg); ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes()); channel.write(outBuffer);// Send the message back to the client } /** * Start the server test */ public static void main(String[] args) throws IOException { NIOServer server = new NIOServer(); server.initServer(8000); server.listen(); } }
2. Client
public class NIOClient { //Selector private Selector selector; /** * Do some initialization work for the channel */ public void initClient(String ip,int port) throws IOException { // Get a Socket channel SocketChannel channel = SocketChannel.open(); // set the channel to non-blocking channel.configureBlocking(false); // Selector this.selector = Selector.open(); // The client connects to the server. In fact, the method execution does not realize the connection. It needs to be adjusted in the listen() method. //Use channel.finishConnect(); to complete the connection channel.connect(new InetSocketAddress(ip,port)); //Bind the channel manager to the channel and register the SelectionKey.OP_CONNECT event for the channel. channel.register(selector, SelectionKey.OP_CONNECT); } /** * Use polling to monitor whether there are events that need to be processed on the selector, and if so, process them */ @SuppressWarnings("unchecked") public void listen() throws IOException { // polling access selector while (true) { selector.select(); // Get the iterator of the selected items in the selector Iterator ite = this.selector.selectedKeys().iterator(); while (ite.hasNext()) { SelectionKey key = (SelectionKey) ite.next(); // delete the selected key to prevent repeated processing ite.remove(); // connect event occurs if (key.isConnectable()) { SocketChannel channel = (SocketChannel) key .channel(); // If connecting, complete the connection if(channel.isConnectionPending()){ channel.finishConnect(); } // set to non-blocking channel.configureBlocking(false); //You can send information to the server here channel.write(ByteBuffer.wrap(new String("abc").getBytes())); //After the connection with the server is successful, in order to receive the information from the server, you need to set the read permission for the channel. channel.register(this.selector, SelectionKey.OP_READ); // get readable events } else if (key.isReadable()) { read(key); } } } } /** * Processing logic */ public void read(SelectionKey key) throws IOException{ SocketChannel channel = (SocketChannel) key.channel(); // create read buffer ByteBuffer buffer = ByteBuffer.allocate(10); channel.read(buffer); byte[] data = buffer.array(); String msg = new String(data).trim(); System.out.println("Client received message: "+msg); } /** * Start client test */ public static void main(String[] args) throws IOException { NIOClient client = new NIOClient(); client.initClient("localhost",8000); client.listen(); } }