0 takeaway

Original article, reprint please indicate the source.

This article source address: netty-source-code-Analysis

Those of you who have used Java are probably familiar with BIO and NIO. Even if you haven’t seen them in your daily work, you will have seen them in recruitment requirements or have been asked about them in interviews. A BIO is a blocking IO and NIO is none blocking IO. A BIO is a blocking IO and NIO is none blocking IO. A BIO is a blocking IO and NIO is none blocking IO.

1 BIO hello word

1.1 BIO Server

/** * Welcome to follow the public account "kind code", get in-depth communication on wechat of bloggers **@author wangjianxin
 */
public class HelloBioServer {
    public static void main(String[] args) throws IOException {
        / / create a ServerSocket
        ServerSocket serverSocket = new ServerSocket();
        // Bind to port 8000
        serverSocket.bind(new InetSocketAddress(8000));
        newBioServerConnector(serverSocket).start(); }}Copy the code
/** * Welcome to follow the public account "kind code", get in-depth communication on wechat of bloggers **@author wangjianxin
 */
public class BioServerConnector {
    private final ServerSocket serverSocket;

    public BioServerConnector(ServerSocket serverSocket) {
        this.serverSocket = serverSocket;
    }

    public void start(a) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run(a) {
                while (true) {
                    Socket newSocket = null;
                    try {
                        // Block here waiting for a new connection and return a new connection
                        newSocket = serverSocket.accept();
                    } catch (IOException e) {
                    }
                    // Turn the new connection over to handler
                    newBioServerHandler(newSocket).start(); }}}); thread.start(); }}Copy the code
/** * Welcome to follow the public account "kind code", get in-depth communication on wechat of bloggers **@author wangjianxin
 */
public class BioServerHandler {
    private final Socket socket;

    public BioServerHandler(Socket socket) {
        this.socket = socket;
    }

    public void start(a) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run(a) {
                while (true) {
                    try {
                        InputStream inputStream = socket.getInputStream();
                        byte[] buffer = new byte[1024];
                        // Block operation, because you don't know when the inputStream will be available for reading, you can only block here and wait
                        // Each connection consumes one thread
                        int readLength = inputStream.read(buffer);
                        if(readLength ! = -1) {
                            String name = new String(buffer, 0, readLength);
                            System.out.println(name);
                            // Print the data sent by the client
                            socket.getOutputStream().write(("hello, "+ name).getBytes()); }}catch (Throwable e) {
                        try {
                            socket.close();
                        } catch(IOException ioException) { } } } } }); thread.start(); }}Copy the code

The bio server consists of three classes: HelloBioServer, BioServerConnector, and BioServerHandler.

  • HelloBioServer: Start the class, create a ServerSocket, and hand it over to BioServerConnetor.
  • BioServerConnector: A class that accepts new connections by creating a thread that loops around the ServerSocket waiting for a new connection. Each time a new connection is made, a BioServerHandler is created and handed over to the BioServerHandler to handle the connection.
  • BioServerHandler: A class that handles data on the connection. Each BioServerHandler creates a thread loop that blocks on the Socket’s InputStream, reads the data, and sends it back with a “Hello,” spell.

1.2 BIO client

/** * Welcome to follow the public account "type code", * * @author wangjianxin */ public class HelloBioClient {private static final int CLIENTS = 2; public static void main(String[] args) throws IOException { for (int i = 0; i < CLIENTS; i++) { final int clientIndex = i; Thread client = new Thread(new Runnable() {@override public void run() {try {// create socket socket socket = new socket ();  Socket. Connect (new InetSocketAddress(8000)); while (true) { OutputStream outputStream = socket.getOutputStream(); Outputstream. write(("zhongdaima" + clientIndex).getBytes())); // Send "zhongdaima" + client number to the server. InputStream inputStream = socket.getInputStream(); byte[] buffer = new byte[1024]; int readLength = inputStream.read(buffer); System.out.println(new String(buffer, 0, readLength)); Thread.sleep(1000); } } catch (Throwable e) { } } }); client.start(); }}}Copy the code

The BIO client has one class, HelloBioClient, and creates two threads and two connections in the client. Each thread processes one connection, sends “zhongdaima” + client number to the server in a loop, and prints the data returned by the server.

2 NIO hello word

2.1 NIO Server

/** * Welcome to follow the public account "kind code", to get in-depth communication with bloggers on wechat *@author wangjianxin
 */
public class HelloNioServer {
    public static void main(String[] args) throws IOException {
        / / create a ServerSocketChannel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // Set Channel to non-blocking
        serverSocketChannel.configureBlocking(false);
        // Bind to port 8000
        serverSocketChannel.bind(new InetSocketAddress(8000));
        / / to the Connector
        newNioServerConnector(serverSocketChannel).start(); }}Copy the code
/** * Welcome to follow the public account "kind code", to get in-depth communication with bloggers on wechat *@author wangjianxin
 */
public class NioServerConnector {
    private final ServerSocketChannel serverSocketChannel;

    private final Selector selector;

    private final NioServerHandler nioServerHandler;

    public NioServerConnector(ServerSocketChannel serverSocketChannel) throws IOException {
        this.selector = Selector.open();
        this.serverSocketChannel = serverSocketChannel;
        // Register a Channel with a selector. The event of interest is OP_ACCEPT.
        this.serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT, serverSocketChannel);
        this.nioServerHandler = new NioServerHandler();
        this.nioServerHandler.start();
    }

    public void start(a) {
        Thread serverConnector = new Thread(new Runnable() {
            @Override
            public void run(a) {
                while (true) {
                    try {
                        if (NioServerConnector.this.selector.select() > 0) {
                            Set<SelectionKey> selectionKeys = NioServerConnector.this.selector.selectedKeys();
                            Iterator<SelectionKey> iterator = selectionKeys.iterator();
                            while (iterator.hasNext()) {
                                SelectionKey key = iterator.next();
                                try {
                                    if (key.isAcceptable()) {
                                        // A new connection is added
                                        SocketChannel socketChannel = ((ServerSocketChannel) key.attachment()).accept();
                                        socketChannel.configureBlocking(false);
                                        // Pass the new connection to the serverHandler
                                        NioServerConnector.this.nioServerHandler.register(socketChannel); }}finally{ iterator.remove(); }}}}catch(IOException e) { } } } }); serverConnector.start(); }}Copy the code
/** * Welcome to follow the public account "kind code", to get in-depth communication with bloggers on wechat *@author wangjianxin
 */
public class NioServerHandler {
    private final Selector selector;

    private final BlockingQueue<SocketChannel> prepareForRegister = new LinkedBlockingDeque<>();

    public NioServerHandler(a) throws IOException {
        this.selector = Selector.open();
    }

    public void register(SocketChannel socketChannel) {
        // Why don't you just register it? Register blocks when a thread selects on a selector
        // If a channel has never been registered, the thread in the start method will always block, and the thread calling register here will also block
        // So we put the channel to be registered in the queue, and we wake up the thread in the start method, so that the thread in the start method is deregister

        // Put it into the queue to be registered
        try {
            this.prepareForRegister.put(socketChannel);
        } catch (InterruptedException e) {

        }
        // Wake up the thread blocking on selector (that is, the thread created in the start method below)
        this.selector.wakeup();

    }

    public void start(a) {
        Thread serverHandler = new Thread(new Runnable() {
            @Override
            public void run(a) {
                try {
                    while (true) {
                        Only one thread is needed to monitor all connections
                        // When the select method returns a value greater than 0, the Channels registered with the selector have events of interest
                        // The return value represents the number of channels in which the event of interest occurred
                        if (NioServerHandler.this.selector.select() > 0) {
                            // Then call the selectedKeys method to get the set of keys where the event occurred
                            Set<SelectionKey> selectionKeys = NioServerHandler.this.selector.selectedKeys();
                            Iterator<SelectionKey> iterator = selectionKeys.iterator();
                            // Iterate over the Key set to process Channel IO events
                            while (iterator.hasNext()) {
                                SelectionKey key = iterator.next();
                                try {
                                    if (key.isReadable()) {
                                        SocketChannel socketChannel = (SocketChannel) key.attachment();
                                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                                        int readLength = socketChannel.read(buffer);
                                        buffer.flip();
                                        System.out.println(new String(buffer.array(), 0, readLength));
                                        socketChannel.write(ByteBuffer.wrap(("hello, " + new String(buffer.array(), 0, readLength)).getBytes())); }}finally {
                                    iterator.remove();
                                }
                            }
                        }
                        SocketChannel socketChannel;
                        while ((socketChannel = NioServerHandler.this.prepareForRegister.poll()) ! =null) {
                            // Register the channel to be registered. The event of interest is OP_READ.
                            socketChannel.register(NioServerHandler.this.selector, SelectionKey.OP_READ, socketChannel); }}}catch(IOException e) { } } }); serverHandler.start(); }}Copy the code

Like the BIO server, the NIO server has three classes: HelloNioServer, NioServerConnector, and NioServerHandler.

  • HelloNioServer: Launch class, create a ServerSocketChannel, set the Channel to non-blocking, bind it to port 8000, and hand it over to the Connector. And now we should see why NIO is None blocking, because it’s one more step than BIO, which is to set the Channel to non-blocking. So let’s move on to where we see non-blocking.

  • NioServerConnector: Class that handles new connections. This class receives a ServerSocketChannel, creates a Selector, registers the Channel with the Selector, is interested in OP_ACCEPT (new connection access), and creates an instance of NioServerHandler. Create a thread in the NioServerConnector’s start method that loops to the selector to ask if a new connection has been entered. Once a new connection is found, it is handed over to the NioServerHandler. Unlike the BIOServerConnector, there is no need to create a new Handler when a new connection comes in. Instead, all connections share one Handler.

  • NioServerHandler: Class that handles connection data. This class creates a thread loop in the start method to ask the Selector if a readable event has occurred. The socketChannel-read (buffer) method reads data from a connection as soon as a readable event has occurred on the connection. The socketChannel-read (buffer) method does not block and adds a “Hello,” to the data and sends it back. The new connection registration is then processed, registering the new connection to the Selector, and the event of interest is OP_READ (readable event). Unlike BioServerHandler, where a thread can handle only one connection, NioServerHandler can handle multiple connections.

Ok, so far we have seen that NIO is non-blocking in the socketChannel.read() method, and BIO is blocking in the inputStream.read () method.

2.2 NIO Client

/** * Welcome to follow the public account "kind code", get in-depth communication on wechat of bloggers **@author wangjianxin
 */
public class HelloNioClient {
    private static final int CLIENTS = 2;

    public static void main(String[] args) throws IOException {
        Thread client = new Thread(new Runnable() {
            final Selector selector = Selector.open();
            final SocketChannel[] clients = new SocketChannel[CLIENTS];

            @Override
            public void run(a) {
                // Create two clients
                for (int i = 0; i < CLIENTS; i++) {
                    try {
                        // Connect port 8000
                        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress(8000));
                        // Set channel to non-blocking
                        socketChannel.configureBlocking(false);
                        // Register selector
                        socketChannel.register(this.selector, SelectionKey.OP_READ, socketChannel);
                        / / save the channel
                        clients[i] = socketChannel;
                    }catch (Throwable e){

                    }
                }
                for (int i = 0; i < Integer.MAX_VALUE; i++) {
                    try {
                        // Send "zhongdaima" + client number to the server
                        for (int j = 0; j < clients.length; j++) {
                            this.clients[j].write(ByteBuffer.wrap(("zhongdaima" + j).getBytes()));
                        }
                        // Monitor the Channel for readable events
                        if (this.selector.select() > 0) {
                            Set<SelectionKey> selectionKeys = this.selector.selectedKeys();
                            Iterator<SelectionKey> iterator = selectionKeys.iterator();
                            while (iterator.hasNext()){
                                SelectionKey key = iterator.next();
                                try {
                                    SocketChannel channel = (SocketChannel) key.attachment();
                                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                                    int read = channel.read(buffer);
                                    buffer.flip();
                                    // Print the data returned by the server
                                    System.out.println(new String(buffer.array(), 0, read));
                                }finally {
                                    iterator.remove();
                                }
                            }
                        }
                        Thread.sleep(1000);
                    }catch(Throwable e){ } } } }); client.start(); }}Copy the code

The NIO client has only one class, HelloNioClient, which creates a thread, two connections, and loops to send “zhongdaima” + client number to the server, and then asks Selctor if any readable events have occurred. Once a readable event has occurred, the data is read and printed. Unlike HelloBioClient, HelloNioClient creates two connections but uses only one thread, whereas HelloBioClient creates two connections and uses two threads.

3 What are the advantages of NIO over BIO

Here’s a look at NIO’s advantages over BIO. What’s the advantage? First of all, let’s see what the differences are. I have shown the differences in bold above code, and the first impression is that NIO saves more threads than BIO.

3.1 BIO model

Here’s a diagram of the BIO. It’s pretty simple. Each connection needs one thread to handle it, because you can’t tell when data is available in the connection, you just have to wait.

3.2 NIO model

This is a schematic of NIO, which has one more component Selector than BIO. It’s the existence of selectors that makes NIO fundamentally different from BIO. In BIO, a thread blocks directly on one connection and does not return until there is data to read, and in NIO, a thread blocks first on a Selector, on which multiple connections can be registered.

The thread calls the select method to ask the Selector if an event of interest has occurred, blocks on the select method, and returns only if an event has occurred on one or more connections. At this point, the thread already knows which connections have events and processes them. Once that’s done, block again on the select method of the Selector, and so on.

So far we have seen that the essential difference between BIO and NIO is that there is an additional layer of proxy Selector in between, and the Selector has the ability to monitor multiple connections.

3.3 Give an example

Open a restaurant in the BIO model, where there is only one chef (Thread) and one customer (connection) to eat, and the chef cooks for this customer until the customer checks out (connection closed), and then the chef starts cooking for the next customer. If you need to feed 10 customers at the same time, you need 10 chefs.

Open a niO-style restaurant with a chef (Thread) and a waiter (Selector), 10 customers, and the waiter orders for those 10 customers (register with Selector), And you need to know what your customers are ordering (the event of interest when registering with the Selector). The chef asks the waiter what the customer has ordered (Selector. Select ()), starts cooking, and then asks the waiter what the customer has ordered, and so on. It only needs one chef and one waiter to serve multiple customers.

Obviously, if you run a restaurant, do you run a BIO restaurant or do you run a NIO restaurant.

4 summarizes

NIO is non-blocking in the socketChannel.read() method, while BIO is blocking in the inputStream.read () method.

NIO can handle multiple connections per thread, while BIO can handle only one connection per thread. NIO is more threading efficient.


About the author

Wang Jianxin, senior Java engineer of architecture department, mainly responsible for service governance, RPC framework, distributed call tracking, monitoring system, etc. Love technology, love learning, welcome to contact and exchange.