Mp.weixin.qq.com/s/AXXMyteVB…

So first of all, what are BIO, NIO, AIO?


BIO: The traditional network communication model, known as BIO, synchronously blocks IO


It is actually a server to create a ServerSocket, and then the client with a Socket to connect to the server ServerSocket, ServerSocket received a connection request to create a Socket and a thread to communicate with that Socket.


The client and server then block communication, the client sends a request, and the server Socket processes it and returns a response.


The client side is blocked and waiting until the response comes back.


The downside of this approach is that each time a client is connected, a thread needs to be created on the server to service the client


In this way, when a large number of clients come, the number of threads on the server side may reach thousands or even tens of thousands. In this way, the server side may be overloaded and finally crash and die.

BIO model diagram:


Acceptor:

There are two classic design modes in the traditional IO model of network service: one is multithreading, the other is to rely on thread pool for processing.


This is the Acceptor thread model if it is based on multithreading.


NIO:

NIO is a synchronous non-blocking IO based on the Reactor model.


In fact, it is equivalent to a thread processing a large number of client requests, through a thread polling a large number of channels, each time to obtain a batch of channels with events, and then start a thread processing for each request.


So the core of this is non-blocking, just that selector a thread can poll a channel over and over again, and all the client requests don’t block, they just come in, they just wait in line.


The core of this optimization BIO is that a client does not interact with data all the time, so there is no need for a thread to be idle, so the client chooses to let the thread rest and only initiate notification when the client has a corresponding action, creating a thread to handle the request.

NIO: Model diagram



Reactor model:


AIO

AIO: asynchronous non-blocking IO, based on Proactor model.


For each connection request, a Buffer is bound and the operating system is told to complete the asynchronous read, while you can do other things


When the operating system finishes reading, it calls your interface and gives you the data that the operating system reads asynchronously. That’s when you can take the data and process it, and write it back


In the process of writing back, the operating system is also given a Buffer, the operating system to complete the write, the completion of the write to inform you.


Both processes have buffers, through which data is read and written.


The main difference here is that once the data is written to the buffer, it is left to the operating system to do the rest.


The operating system writes data back to Buffer, and then notifies the client to read the data.


AIO: Model diagram



Having talked about the differences between BIO, NIO, and AIO, let’s now combine these three models to talk about synchronization and blocking.


A synchronized block

Why is BIO blocked synchronously?


In fact, this is not for the network communication model, but for disk file read and write IO operations.


Because reading and writing from BIO streams, like FileInputStrem, means you make an IO request and hang there, having to wait for the IO to complete before you can return.


Synchronous non-blocking:

Why NIO is synchronous non-blocking?

Because no matter how many clients can access the server, it doesn’t take a thread to access the client, it just creates a connection and registers it with the selector, and then you can do whatever else you want to do


A selector thread constantly polls all socket connections, notifies you when it sees an event, and then you start a thread to process a request, which is non-blocking.


But in this process, you still have to read the data, process it, and return it, and it’s synchronous.


Asynchronous nonblocking

Why is AIO asynchronous and non-blocking?


Once you do a file IO operation with AIO, you immediately go back and do something else, and then you don’t have to worry about it, and the operating system tells you it’s ok


When you read and write files based on AIO’s API, after you make a request, the rest is left to the operating system


When the read/write is complete, the operating system will call your interface back and forth to tell you that the operation is complete


Instead of waiting and polling to determine the status of the operating system, you can do other things.


Synchronous means you have to poll the operating system, asynchronous means the operating system notifies you. So AIO is asynchronous and non-blocking.


NIO core components are explained in detail


Learn about NIO first to understand some related concepts, NIO communication components, what are the corresponding functions, what is the relationship between them?


The multiplexing mechanism implements Selector


First, let’s understand the traditional Socket network communication model.


Traditional Socket communication schematic diagram


Why do traditional sockets not support mass connections?


Each time a client is connected, a thread is created on the server to serve the client


This will lead to a large number of clients, the number of threads on the server side may reach thousands or even tens of thousands, hundreds of thousands, which will lead to the server side program load is too high, overwhelmed, and finally the system crashes and dies.


Let’s take a look at how NIO implements the massive connections supported by the multiplexing mechanism based on selectors.


The principle diagram of the NIO


How does the multiplexing mechanism support mass connections?


NIO’s threading model does not need to create a thread for each connection initiated by a Socket, but can use a Selector to multiplex and listen for N channels to see if there is a request, whether the request is the corresponding connection request or the request to send data


This is based on the underlying Select notification mechanism of the operating system. A Selector constantly polls multiple channels, which avoids creating multiple threads


A thread will only be created if a Channel has a corresponding request, maybe 1000 requests, and only 100 requests will have data interaction


At this point, the server may provide 10 threads to handle the request. This way, you can avoid creating lots of threads.


How does NIO Buffer data with buffers

What is a Buffer in NIO?


The first step in learning NIO is to understand the so-called Buffer Buffer, which is a core part of NIO


In general, if you are writing data to a file or network through NIO, or reading data from a file or network, you need to use the Buffer Buffer to do so. There are generally several steps to use Buffer:


Write data to Buffer, call the flip() method, read data from Buffer, and call the clear() or Compact () methods.


What are the corresponding positions, Mark, Capacity, and Limit in Buffer?


  • Capacity: indicates the capacity of the buffer, that is, the size of the data contained in it.

  • Limit: A limit on the use of the buffer buffer from which no data can be read.

  • Position: indicates the index in the array that can be read or written. The value cannot be greater than limit.

  • Mark: it is similar to a landmark. When you set the mark in a position, you can set a mark

    A subsequent call to the reset() method resets position to the mark that was set at the time. When you set position or limit to a value less than mark, the mark is discarded

    If the Buffer created in Direct mode is used, the intermediate Buffer is reduced and the data is stored directly using DirectorBuffer.

How does Channel and FileChannel read Buffer data and write it to disk

What is a Channel in NIO?

A Channel is a data Channel in NIO, similar to a stream but somewhat different


A Channel can either read data from it or write data to it, but reads and writes to streams are usually one-way.


A Channel can be read and written asynchronously. Data in a Channel is always read to a Buffer first, or written to a Channel from the Buffer.

What does a FileChannel do?

Buffers have different types, and channels have several types.


  • FileChannel

  • DatagramChannel

  • SocketChannel

  • ServerSocketChannel


These channels cover UDP and TCP network IO, as well as file IO. The FileChannel is the channel that corresponds to the file IO and is used to read the file.


Here is a simple NIO implementation to read file Demo code


Public class FileChannelDemo1 {public static void main(String[] args) throws Exception {// Constructs a traditional file output stream FileOutputStream out = new FileOutputStream("F:\development\tmp\hello.txt"); FileChannel = out.getChannel(); FileChannel = out.getChannel(); ByteBuffer = bytebuffer.wrap ("hello world".getBytes()); // Write the Buffer to the output stream via FileChannel, persisting it to disk channel.write(Buffer); channel.close(); out.close(); }}Copy the code
Copy the code


NIOServer and Client code cases


Finally, give you a NIO client and server example code, a simple experience of NIO communication.


  • NIO communication Client


import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.util.Iterator; public class NIOClient { public static void main(String[] args) {for(int i = 0; i < 10; i++){             new Worker().start();         }           }     static class Worker extends Thread {    @Override      public void run() { SocketChannel channel = null; Selector selector = null; Try {// SocketChannel = socketchannel.open (); / / a SocketChannel is connected to the underlying Socket network / / data channel is responsible for channel based on the network, speaking, reading and writing data. ConfigureBlocking (false);          channel.connect(new InetSocketAddress("localhost", 9000)); Selector = selector. Open (); Channel. Register (selector, selectionkey.op_connect);while(trueSelect () {// the selector multiplexing mechanism implements a loop through each registered Channel selector. Iterator<SelectionKey> keysIterator = selector.selectedKeys().iterator();while(keysIterator.hasNext()){ SelectionKey key = (SelectionKey) keysIterator.next(); keysIterator.remove(); // If a connectable message is found on return, it goes below to receive dataif(key.isConnectable()){                channel = (SocketChannel) key.channel();               if(channel.isConnectionPending()){ channel.finishConnect(); // Once the connection has been established, the SocketChannel server can send the data to you. ByteBuffer = bytebuffer.allocate (1024); buffer.put("Hello".getBytes()); buffer.flip(); channel.write(buffer); } channel.register(selector, SelectionKey.OP_READ); } // The server has returned a piece of data that can be readelse if(key.isReadable()){ channel = (SocketChannel) key.channel(); ByteBuffer = ByteBuffer. Allocate (1024); Int len = channel.read(buffer); int len = channel.read(buffer);if(len > 0) {                System.out.println("[" + Thread.currentThread().getName()                     + "] Received a response:"+ new String(buffer.array(), 0, len)); Thread.sleep(5000); channel.register(selector, SelectionKey.OP_WRITE); }}else if(key.isWritable()) {                    ByteBuffer buffer = ByteBuffer.allocate(1024);                    buffer.put("Hello".getBytes());                    buffer.flip();                    channel = (SocketChannel) key.channel();                    channel.write(buffer);                           channel.register(selector, SelectionKey.OP_READ);                  }           }          }                                  } catch (Exception e) {          e.printStackTrace();        } finally{          if(channel != null){            try {              channel.close();            } catch (IOException e) {                                    e.printStackTrace();            }                            }          if(selector ! = null){ try { selector.close(); } catch (IOException e) { e.printStackTrace(); } } } } }}Copy the code


  • NIO communication Server


import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; public class NIOServer { private static Selector selector; private static LinkedBlockingQueue<SelectionKey> requestQueue; private static ExecutorService threadPool; public static void main(String[] args) { init(); listen(); } private static voidinit(){ ServerSocketChannel serverSocketChannel = null; try { selector = Selector.open(); serverSocketChannel = ServerSocketChannel.open(); / / set the Channel to non-blocking NIO is to support the non-blocking serverSocketChannel. ConfigureBlocking (false); serverSocketChannel.socket().bind(new InetSocketAddress(9000), 100); / / ServerSocket, to each client connection is responsible for the connection request serverSocketChannel. Register (selector, SelectionKey. OP_ACCEPT); } catch (IOException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace(); } requestQueue = new LinkedBlockingQueue<SelectionKey>(500); threadPool = Executors.newFixedThreadPool(10);for(int i = 0; i < 10; i++) {          threadPool.submit(new Worker());          }    }    private static void listen() {          while(true){              try{                  selector.select();                               Iterator<SelectionKey> keysIterator = selector.selectedKeys().iterator();                  while(keysIterator.hasNext()){ SelectionKey key = (SelectionKey) keysIterator.next(); // a SelectionKey represents a request keysiterator.remove (); handleRequest(key); } } catch(Throwable t){ t.printStackTrace(); } } } private static void handleRequest(SelectionKey key) throws IOException, ClosedChannelException {// Threads in the background thread pool handle the following code logic SocketChannel channel = null; Try {// If this Key is an acceptable request, it is a connection requestif(key.isAcceptable()){ ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel(); / / call the accept this method can be a channel = serverSocketChannel TCP three-way handshake. The accept (); / / shake hands are successful to get a TCP connection good SocketChannel channel. ConfigureBlocking (false); channel.register(selector, SelectionKey.OP_READ); } // If the key is readable, the data sent by the client will need to be READelse if(key.isReadable()){ channel = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); int count = channel.read(buffer); // If you read the data through the underlying socket, position will change to something like 21. // If you read the data through the underlying socket, position will change to something like 21if(count > 0){// Ready to read the data just written, that is, will belimitSet to current position, set position to 0, and discard mark. Flip // position = 0, flip // position = 0limitFlip (); flip(); flip(); System.out.println("Server receives request:"+ new String(buffer.array(), 0, count)); channel.register(selector, SelectionKey.OP_WRITE); }}else if(key.isWritable()) {              ByteBuffer buffer = ByteBuffer.allocate(1024);              buffer.put("Receive".getBytes());              buffer.flip();              channel = (SocketChannel) key.channel();              channel.write(buffer);                     channel.register(selector, SelectionKey.OP_READ);            }        }          catch(Throwable t){              t.printStackTrace();              if(channel ! = null){ channel.close(); Static class Worker implements Runnable {@override public void implements Runnablerun() {      while(true) { try { SelectionKey key = requestQueue.take(); handleRequest(key); } catch (Exception e) { e.printStackTrace(); }} private void handleRequest(SelectionKey key) throws IOException, ClosedChannelException { SocketChannel = null; SocketChannel = null; Try {// If this key is acceptable, it is the connection requestif(key.isAcceptable()){                 System.out.println("[" + Thread.currentThread().getName() + "] Connection request received"); ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel(); / / call the accept method to shake hands with the client has undergone three channel = serverSocketChannel. The accept (); System.out.println("[" + Thread.currentThread().getName() + "] channel="+ channel); // If the three-way handshake is successful, a TCP connection can be obtained through a SocketChannel. Is connect with the client / / your SocketChannel is connected to the Socket, responsible for network data read/write / / set to the nonblocking channel. The configureBlocking (false); Register (selector, selectionkey.op_read); } // If the key is readable, the data sent by the client will need to be readelse if(key.isReadable()){ channel = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); int count = channel.read(buffer); // If you read the data through the underlying socket and write to the buffer, position will be changed to something like 21 // If you read the number of bytes, position will be changed to system.out.println ("[" + Thread.currentThread().getName() + "] Request received");                          if(count > 0){ buffer.flip(); // position = 0,limitPrintln (system.out.println ());"Server receives request:"+ new String(buffer.array(), 0, count)); channel.register(selector, SelectionKey.OP_WRITE); }}else if(key.isWritable()) {                ByteBuffer buffer = ByteBuffer.allocate(1024);                buffer.put("Receive".getBytes());                buffer.flip();                channel = (SocketChannel) key.channel();                channel.write(buffer);                       channel.register(selector, SelectionKey.OP_READ);              }          }            catch(Throwable t){                t.printStackTrace();                if(channel ! = null){ channel.close(); }}}}}Copy the code
Copy the code


Conclusion:


Through this article, it mainly analyzes some common problems of NIO:


  • BIO, NIO, AIO


  • What is synchronous blocking, synchronous non-blocking, and asynchronous non-blocking


  • Why is NIO able to handle the volume of requests for support


  • Principles of NIO-related components


  • A simple case of NIO communication


This article only introduces some principles of network communication, to explain the interview


NIO communication actually has a lot of things, and it is used very frequently in the research and development of middleware. I will share with you later if I have the opportunity.