Comparison of network IO models (BIO, NIO, AIO)

Posted by dopey on Wed, 09 Feb 2022 19:18:19 +0100

IO overview

The core of software development is data, and the transmission, storage and reading of data are realized through IO technology.

There are three main IO models in Java:

  • BIO synchronous blocking IO
  • NIO synchronous non blocking IO
  • AIO asynchronous non blocking IO

BIO

Blocking IO synchronous blocking IO is a commonly used IO model

Characterized by

  • Writing is relatively simple
  • It is divided into input stream and output stream
  • During network communication, the read operation of the input stream will block the thread until an output stream performs write operation

Once the thread of the server is blocked, the server cannot handle the business of other clients. In order to solve this problem, we usually start the thread separately for each client.

This creates a new problem, that is, if there are many clients, it will consume a lot of thread resources of the server, resulting in excessive pressure on the server.

Therefore, this model is more suitable for the situation that the number of connections is small and the number is relatively fixed.

The following is how to use the Socket of BIO model for network communication

/**
 * BIO The server
 */
public class SocketServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(9000);
        while (true) {
            System.out.println("Waiting for connection..");
            //Blocking method
            Socket socket = serverSocket.accept();
            System.out.println("A client is connected..");
            //Start thread processing client IO
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        handler(socket);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }

    /**
     * Process client IO
     * @param socket
     * @throws IOException
     */
    private static void handler(Socket socket) throws IOException {
        System.out.println("thread id = " + Thread.currentThread().getId());
        byte[] bytes = new byte[1024];

        System.out.println("prepare read. . ");
        //Receive data from the client and block when there is no data readable
        int read = socket.getInputStream().read(bytes);
        if (read != -1) {
            System.out.println("Data received from client:" + new String(bytes, 0, read));
        }
        //Return data to the client
        socket.getOutputStream().write("Hello, client".getBytes());
        socket.getOutputStream().flush();
    }
}
/**
 * client
 */
public class SocketClient {

    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 9000);
        //Send data to the server
        socket.getOutputStream().write("Hello!!".getBytes());
        socket.getOutputStream().flush();
        System.out.println("End of sending data to the server");
        byte[] bytes = new byte[1024];
        //Receive the data returned by the server
        socket.getInputStream().read(bytes);
        System.out.println("Data received from the server:" + new String(bytes));
        socket.close();
    }
}

NIO

jdk1.4. NIO (Noblocking IO synchronous non blocking IO) appears. Different from BIO, NIO will not block threads when reading

NIO has three core components:

  • Channel channel is similar to the function of BIO input / output stream. The channel with different IO streams can complete read and write operations at the same time
  • Buffer buffer, similar to byte array in BIO, is used to store data
  • Selector multiplexer. Each Channel will be registered with the selector. The selector will poll among multiple channels. Once a read-write event occurs, it will be processed immediately

Advantages: a single thread can handle a large number of IO requests, which greatly reduces the pressure on the server.

Disadvantages: if the processing time of each IO request is relatively long, there will be a long queue; Programming is also complex.

It is more suitable for dealing with scenes with a large number of connections and a short time, such as chat, bullet screen, etc.

The server

public class NIOServer {

    public static void main(String[] args) throws IOException {
        // Create a service Socket channel that listens on the local port
        ServerSocketChannel ssc = ServerSocketChannel.open();
        //It must be configured as non blocking to register with the selector
        ssc.configureBlocking(false);
        ssc.socket().bind(new InetSocketAddress(9000));
        // Create selector
        Selector selector = Selector.open();
        // Register on the selector and be interested in client connection operation
        ssc.register(selector, SelectionKey.OP_ACCEPT);
        while (true) {
            // Polling the key in the listening channel. select is blocked
            selector.select();
            System.out.println("Event occurrence");
            // There is a client request, which is polled and monitored
            Iterator<SelectionKey> it = selector.selectedKeys().iterator();
            while (it.hasNext()) {
                SelectionKey key = it.next();
                //Delete the key s processed this time to prevent repeated processing in the next select
                it.remove();
                handle(key);
            }
        }
    }

    /**
     * event processing 
     * @param key
     * @throws IOException
     */
    private static void handle(SelectionKey key) throws IOException {
        //Handling connection events
        if (key.isAcceptable()) {
            System.out.println("Handling connection events");
            //Get ServerSocketChannel
            ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
            //Get the client received by ServerSocketChannel
            SocketChannel sc = ssc.accept();
            sc.configureBlocking(false);
            //Register interested in SocketChannel read events
            sc.register(key.selector(), SelectionKey.OP_READ);
        }
        //Handle read events
        else if (key.isReadable()) {
            System.out.println("Handle read events");
            SocketChannel sc = (SocketChannel) key.channel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            //The read method will not block. The event that the client sends data must have occurred when calling
            int len = sc.read(buffer);
            if (len != -1) {
                System.out.println("Read the data sent by the client:" + new String(buffer.array(), 0, len));
            }
            ByteBuffer bufferToWrite = ByteBuffer.wrap("Hello client".getBytes());
            sc.write(bufferToWrite);
            key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
        }
        //Handle write events
        else if (key.isWritable()) {
            SocketChannel sc = (SocketChannel) key.channel();
            System.out.println("Handle write events");
            key.interestOps(SelectionKey.OP_READ);
        }
    }
}

client

public class NIOClient {
    //Multiplexer
    private Selector selector;

    /**
     * Start client test
     *
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {
        NIOClient client = new NIOClient();
        client.initClient("127.0.0.1", 9000);
        client.connect();
    }

    /**
     * Initialize the Socket and do some work on the channel
     *
     * @param ip   ip address of the connected server
     * @param port The port number of the connected server
     * @throws IOException
     */
    public void initClient(String ip, int port) throws IOException {
        // Obtain a Socket channel
        SocketChannel channel = SocketChannel.open();
        // Set channel to non blocking
        channel.configureBlocking(false);
        // Get a channel manager
        this.selector = Selector.open();

        // The client connects to the server. In fact, the implementation of the method 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 for the channel OP_ Connect event.
        channel.register(selector, SelectionKey.OP_CONNECT);
    }

    /**
     * Use polling to monitor whether there are events to be processed on the selector. If so, process them
     *
     * @throws IOException
     */
    public void connect() throws IOException {
        // Polling access selector
        while (true) {
            selector.select();
            // Gets the iterator of the selected item in the selector
            Iterator<SelectionKey> it = this.selector.selectedKeys().iterator();
            while (it.hasNext()) {
                SelectionKey key = (SelectionKey) it.next();
                // Delete the selected key to prevent repeated processing
                it.remove();
                // Connection event occurs
                if (key.isConnectable()) {
                    SocketChannel channel = (SocketChannel) key.channel();
                    // If you are connecting, complete the connection
                    if (channel.isConnectionPending()) {
                        channel.finishConnect();
                    }
                    // Set to non blocking
                    channel.configureBlocking(false);
                    //You can send information to the server here
                    ByteBuffer buffer = ByteBuffer.wrap("HelloServer".getBytes());
                    channel.write(buffer);
                    //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);
                }
            }
        }
    }

    /**
     * Handle the event of reading the information sent by the server
     *
     * @param key
     * @throws IOException
     */
    public void read(SelectionKey key) throws IOException {
        //It is the same as the read method of the server
        // Socket: get the message that the server can read
        SocketChannel channel = (SocketChannel) key.channel();
        // Create read buffer
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        int len = channel.read(buffer);
        if (len != -1) {
            System.out.println("Client received message:" + new String(buffer.array(), 0, len));
        }
    }
}

AIO

AIO (asynchronous non blocking), after the operating system completes, the callback notifies the server program to start the thread for processing

It is suitable for operation with a large number of connections and a long time. Jdk1 7 start

The server

public class AIOServer {
    public static void main(String[] args) throws Exception {
        //Open the asynchronous server channel and bind the port
        final AsynchronousServerSocketChannel serverChannel =
                AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(9000));
        //Set the server to receive connection callback
        serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
            //Connection successful
            @Override
            public void completed(AsynchronousSocketChannel socketChannel, Object attachment) {
                try {
                    // Receive client connections
                    serverChannel.accept(attachment, this);
                    System.out.println(socketChannel.getRemoteAddress());
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    //Set callback for reading client data
                    socketChannel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
                        //Read successful
                        @Override
                        public void completed(Integer result, ByteBuffer buffer) {
                            buffer.flip();
                            System.out.println(new String(buffer.array(), 0, result));
                            socketChannel.write(ByteBuffer.wrap("HelloClient".getBytes()));
                        }

                        @Override
                        public void failed(Throwable exc, ByteBuffer buffer) {
                            exc.printStackTrace();
                        }
                    });
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //connection failed
            @Override
            public void failed(Throwable exc, Object attachment) {
                exc.printStackTrace();
            }
        });

        Thread.sleep(Integer.MAX_VALUE);
    }
}

client

public class AIOClient {

    public static void main(String[] args) throws Exception {
        AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
        socketChannel.connect(new InetSocketAddress("127.0.0.1", 9000)).get();
        socketChannel.write(ByteBuffer.wrap("HelloServer".getBytes()));
        ByteBuffer buffer = ByteBuffer.allocate(512);
        Integer len = socketChannel.read(buffer).get();
        if (len != -1) {
            System.out.println("Client received message:" + new String(buffer.array(), 0, len));
        }
    }
}

Comparison of BIO, NIO and AIO

BIONIOAIO
IO modelSynchronous blockingSynchronous non blockingAsynchronous blocking
Programming difficultyLowerhigherhigher
reliabilitydifferencegoodgood
throughputlowhighhigh

Take chestnuts for example: go to the restaurant for dinner and wait for a seat

BIO: wait in line at the door after getting the number and do nothing

NIO: after taking the number, go shopping next to it and come to see if you can see it at regular intervals

AIO: after making a reservation on your mobile phone, go shopping next to it, and then go there after you notice on your mobile phone

Topics: Java network server