From BIO to NIO

We know that Java server programming, a very important piece is IO, and our Java IO has gone through the process from BIO to NIO to AIO. First, let's take a look at what is BIO:

1. Ordinary BIOWhen we first started learning Java Socket, we may have written such a piece of code:

ServerSocket serverSocket = new ServerSocket(8000) ;
    While (true){
      Socket socket = serverSocket.accept();
      InputStream is = socket.getInputStream();
      Byte [] buff = new byte[1024];
      Int len ​​= is.read(buff);
      While (len > 0){
        String msg = new String(buff,0,len);
        System.out.println("received" + msg);
        Len = is.read(buff);
      }
    }

Obviously, this code puts the processing after the client accesses into the while loop. That is to say, this code can only handle one client connection for a period of time, then the problem comes, we Think about it, server performance as a server should be good, so this way wastes our performance, and, because the processing of a client connection at a time, the amount of concurrent natural can not go up, so this method will not be produced Used in the environment.

2. Multi-threaded BIO

In order to increase the amount of concurrency, we can change the code to look like this:

    ServerSocket serverSocket = new ServerSocket(8000);
    while (true){
      Socket socket = serverSocket.accept();
      new Thread(new Handler(socket)).start();
    }
public class Handler implements Runnable {

  Private Socket socket;

  Public Handler(Socket socket) {
    This.socket = socket;
  }

  @Override
  Public void run() {
    InputStream is = null;
    Try {
      Is = socket.getInputStream();
      Byte[] buff = new byte[1024];
      Int len ​​= is.read(buff);
      While (len > 0) {
        String msg = new String(buff, 0, len);
        System.out.println("received" + msg);
        Len = is.read(buff);
      }
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      If (is != null) {
        Try {
          Is.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
  }
}

But this will have a problem, we will come to a client connection regardless of the three seven twenty-one will be a new thread, so the number of threads and the number of client connections is 1:1 relationship, we know that the thread Creating and destroying is a very resource intensive thing, as such, it also increases the overhead of thread context switching. In order to reduce the overhead of thread creation and destruction, we can introduce a thread pool (the code is not posted first), but this does not fundamentally solve the problem. In order to solve the problem fundamentally, Java introduced NIO:

3.NIO

We can understand this, we compare the web server to the bank, the client-initiated connection is like the customer who wants to handle the business, the bank handles the business staff As a thread that handles client connections, since the bank's resources (if only one computer can handle the business) are limited, only one person can handle the business at a time. Then our BIO is equivalent to: when a customer comes in, it is assigned a salesman, but the salesman only has access to resources (computer) to handle the business, so it is useless for the customer to assign him a salesperson as soon as he enters the bank. Also wasting resources (salesman is going to get paid) And our NIO is equivalent to only one salesman, but there is a numbering system. The customer enters the bank to register in the numbering system, receive a number, and wait until it is the turn of a customer to handle the business. Salesman, dealing with his business.

public class NIOServer {
  private int port = 8000;
  private Selector selector = null;
  private Charset charset = Charset.forName("UTF-8");
  public NIOServer(int port) throws IOException {
    this.port = port;
    ServerSocketChannel server = ServerSocketChannel.open();
    server.bind(new InetSocketAddress(this.port));
    server.configureBlocking(false);
    selector = Selector.open();
    server.register(selector, SelectionKey.OP_ACCEPT);
  }

  public void listener() throws IOException {
    while (true) {
      int wait = selector.select();
      if (wait == 0) continue;
      Set<SelectionKey> keys = selector.selectedKeys();
      Iterator<SelectionKey> iterator = keys.iterator();
      while (iterator.hasNext()) {
        SelectionKey key = iterator.next();
        iterator.remove();
        process(key);
      }
    }
  }

  public void process(SelectionKey key) throws IOException {
    if (key.isAcceptable()) {
      ServerSocketChannel server = (ServerSocketChannel) key.channel();
      SocketChannel client = server.accept();
      client.configureBlocking(false);
      client.register(selector, SelectionKey.OP_READ);
    }
    if (key.isReadable()) {
      SocketChannel client = (SocketChannel) key.channel();
      ByteBuffer buff = ByteBuffer.allocate(1024);
      StringBuilder content = new StringBuilder();
      try {
        while (client.read(buff) > 0) {
          buff.flip();
          content.append(charset.decode(buff));
        }
        System.out.println(content);
        key.interestOps(SelectionKey.OP_READ);
      } catch (IOException io) {
        key.cancel();
        if (key.channel() != null) {
          key.channel().close();
        }
      }
    }
  }

  public static void main(String[] args) throws IOException {
    new NIOServer(8000).listener();
  }
}

NIO, that is, we come to a client connection, do not process first, first register on the Selector, and then continuously poll the channel registered on the Selector, when ready to read or write, then to the client The connection operates, which saves a lot of resources on the server side, so it is very suitable for a large number of long connection scenarios.