5, 05 [Java IO model] BIO NIO AIO introduction

IO model

The IO model refers to what channel is used to send and receive data.

In the book Unix network programming, five IO models are mentioned, namely, blocking IO, non blocking IO, multiplexing IO, signal driven IO and asynchronous io

Java supports three network programming IO modes: BIO (blocking IO), NIO (non blocking IO), AIO (asynchronous non blocking IO)

BIO(Blocking IO)

Synchronous blocking IO means that a client connection corresponds to a processing thread.

When I first learned Java, I / O flows belonged to the BIO model.

shortcoming

1) Read operation in IO code is blocking operation. If the connection does not read and write data, it will lead to thread blocking and waste resources.

2) If there are many threads, it will lead to too many server threads and too much pressure.

Application scenario

BIO mode is applicable to the architecture with small and fixed connections. This mode requires high server resources, but the program is simple and easy to understand.

Code example

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * Socket Stream socket server
 * ...
 */
public class SocketServer {

    public static void main(String[] args) throws IOException {
        // Create a server socket and bind to the specified port.
        ServerSocket serverSocket = new ServerSocket(9999);

        // Dead circulation ensures that the server is always running online
        while (true) {
            System.out.println("Server waiting for connection:" + System.currentTimeMillis());
            // Listen to the connection to be established to this socket and accept it. This method will block until a connection is established.
            final Socket socket = serverSocket.accept();
            System.out.println("Listen to the client and establish a connection");
            new Thread(new Runnable() {
                public void run() {
                    try {
                        handler(socket);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }

    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");
        // Receiving data from the client is also a blocking method. Blocking occurs when there is no data readable
        int read = socket.getInputStream().read(bytes);
        System.out.println("read complete");
        if (read != -1) {
            System.out.println("Data received from client:" + new String(bytes, 0, read));
            System.out.println("Thread ID = " + Thread.currentThread().getId());
        }
        // Write out data in response to the client
        socket.getOutputStream().write("HelloClient".getBytes());
        socket.getOutputStream().flush();
    }
}
import java.io.IOException;
import java.net.Socket;

/**
 * Socket Stream socket client
 * ...
 */
public class SocketClient {

    public static void main(String[] args) throws IOException {
        // Create a stream socket and connect it to the specified host port
        Socket socket = new Socket("localhost", 9999);

        // Send data to the server
        socket.getOutputStream().write("HelloServer".getBytes());
        socket.getOutputStream().flush();
        System.out.println("End of sending data from client to server!");
        byte[] bytes = new byte[1024];

        // Receive server response data
        socket.getInputStream().read(bytes);
        System.out.println("Received data from the server:" + new String(bytes));

        // Close stream socket
        socket.close();
    }
}

BIO model

 NIO(Non Blocking IO)

Synchronous non blocking io. The server implementation mode is that one thread can process multiple requests (connections). The connection requests sent by the client will be registered on the multiplexer selector, and the multiplexer will process the IO requests after polling the connection. The bottom layer of I/O multiplexing is generally implemented by Linux API s (select, poll, epoll). Their differences are as follows:

Application scenario

NIO mode is applicable to the architecture with a large number of connections and relatively short connections (light operation), such as chat server, barrage system, communication between servers, and complex programming. JDK1.4 starts to support it;

NIO model

NIO has three core components: buffer, channel and selector

1) Channels are similar to streams. Each channel corresponds to a buffer, and the underlying buffer is an array

2) The channel will be registered with the selector, and the selector will send it to an idle thread to process according to the occurrence of the channel read-write event

3) A selector can correspond to one or more threads

4) NIO's Buffer and channel can be read or written

Code example

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.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

/**
 * NIO 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();
        // Set non blocking mode. Otherwise, an error will be reported. The selector mode itself is a non blocking mode
        ssc.configureBlocking(false);
        // Binding port
        ssc.socket().bind(new InetSocketAddress(9999));
        // Create a selector
        Selector selector = Selector.open();
        // Register the ServerSocketChannel to the selector, and the selector is interested in the client accept connection operation
        ssc.register(selector, SelectionKey.OP_ACCEPT);
        // Infinite circulation ensures the online operation of the server
        while (true) {
            System.out.println("The server waits for the event to occur");
            // Poll the key in the listening channel, select is blocked, and accept() is also blocked
            int select = selector.select();
            System.out.println("Something happened="+select);
            // 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 currently processed key to prevent repeated processing in the next select ion
                it.remove();
                // Process the monitored key
                handle(key);
            }
        }
    }

    private static void handle(SelectionKey key) throws IOException {
        if (key.isAcceptable()) {
            System.out.println("Connection event");
            ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
            // NIO non blocking embodiment: the accept method here is blocked, but because a connection event occurred here, this method will be executed immediately without blocking
            // After processing the connection request, it will not continue to wait for the data sent by the client
            SocketChannel sc = ssc.accept();
            sc.configureBlocking(false);
            // Interested in reading events when listening to channels through Selector
            sc.register(key.selector(), SelectionKey.OP_READ);
        } else if (key.isReadable()) {
            System.out.println("Data readable events");
            SocketChannel sc = (SocketChannel) key.channel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            // NIO non blocking embodiment: firstly, the read method will not block. Secondly, in this event response model, when the read method is called, the event that the client sends data must occur
            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("HelloClient".getBytes());
            sc.write(bufferToWrite);
            key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
        } else if (key.isWritable()) {
            SocketChannel sc = (SocketChannel) key.channel();
            System.out.println("write event");
            // NIO event trigger is horizontal trigger
            // When using Java NIO programming, cancel writing events when there is no data to write out,
            // Register the write event when there is data written out
            key.interestOps(SelectionKey.OP_READ);
            // sc.close();
        }
    }
}

NIO server program steps

1) Create a ServerSocketChannel and Selector, and register the ServerSocketChannel with the Selector

2) The selector listens to the channel event through the select() method. When the client connects, the selector listens to the connection event and obtains the selectionKey bound when the ServerSocketChannel is registered

3) selectionKey can obtain the bound ServerSocketChannel through the channel() method

4) ServerSocketChannel gets SocketChannel through accept() method

5) Register SocketChannel on the Selector and pay attention to the read event

6) After registration, a SelectionKey is returned, which is associated with the SocketChannel

7) The selector continues to listen for events through the select() method. When the client sends data to the server, the selector listens to the read event and obtains the selectionKey bound when the SocketChannel is registered

8) selectionKey can obtain the bound socketChannel through the channel() method

9) Read out the data in socketChannel

10) Write the server data back to the client with socketChannel

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;

/**
 * NIO client
 * ...
 */
public class NioClient {

    // Channel Manager
    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", 9999);
        client.connect();
    }

    /**
     * Get a Socket channel, and do some initialization work for this channel
     *
     * @param ip   ip of the connected server
     * @param port Port number of the connected server
     * @throws IOException
     */
    public void initClient(String ip, int port) throws IOException {
        // Get 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 method execution does not realize the connection. You need to call channel in the listen() method finishConnect();  To complete the connection
        channel.connect(new InetSocketAddress(ip, port));
        // Bind the channel manager to the channel and register OP for the channel_ Connect event
        channel.register(selector, SelectionKey.OP_CONNECT);
    }

    /**
     * Listen to whether there are events to be processed on the selector by polling. If there are, process them
     *
     * @throws IOException
     */
    private void connect() throws IOException {
        // Poll access selector
        while (true) {
            // Select a group of events that can be used for I/O operations and put them in the selector. The method of the client will not block,
            // The method here is different from that of the server. Looking at the api annotation, you can see that when at least one channel is selected,
            // The wakeup method of the selector is called, and the method returns. For the client, the channel is always selected
            selector.select();
            // Get the iterator of the selected item in the selector
            Iterator<SelectionKey> it = this.selector.selectedKeys().iterator();
            while (it.hasNext()) {
                SelectionKey key = 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);
                    // Send a message to the server
                    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. Register OP for this channel_ Read event
                    channel.register(this.selector, SelectionKey.OP_READ);
                } else if (key.isReadable()) {
                    // Get the Socket channel of the event
                    SocketChannel channel = (SocketChannel) key.channel();
                    // Create read buffer
                    ByteBuffer buffer = ByteBuffer.allocate(512);
                    int len = channel.read(buffer);
                    if (len != -1) {
                        System.out.println("Client receives message:" + new String(buffer.array(), 0, len));
                    }
                }
            }
        }
    }
}

summary

The selector of NIO model is like a big manager, which is responsible for listening to various IO events and then handing them over to the back-end thread for processing;

NIO is non blocking compared with BIO in that the back-end thread of BIO needs to block waiting for the client to write data (such as the read method). If the client does not write data, the thread will block. NIO hands over the waiting for the client to operate to the selector. The selector is responsible for polling all registered clients. After listening to an event, it will be handed over to the back-end thread for processing. The back-end thread does not need to do any blocking and waiting. It can directly process the data of the client event. After processing, it will end immediately, or return to the thread pool for other client events to continue to use. Also, the reading and writing of the channel is non blocking.

Redis is a typical NIO threading model. The selector collects all connected events and transfers them to the back-end thread. The thread continuously executes all event commands and writes the results back to the client;

 AIO(NIO 2.0)

Asynchronous non blocking. After the operation system completes, the callback notifies the server program to start the thread for processing. It is generally suitable for applications with a large number of connections and a long connection time;

Application scenario

AIO mode is applicable to the architecture with a large number of connections and long connections (re operation), and JDK7 starts to support it;

Code example

Bio&nio&aio comparison

Synchronous & asynchronous

synchronous synchronization is a reliable and orderly operation mechanism. When we synchronize, the subsequent task is to wait for the current call to return before proceeding to the next step.

Asynchronous is the opposite of asynchronous. This task does not need to wait for the current call to return, but realizes the order relationship between tasks by relying on mechanisms such as events and callbacks.

Blocking & non blocking

Blocking: when blocking, the current thread will be in a blocking state and cannot engage in other tasks. It can continue only when the conditions are met, such as the completion of data reading and writing operations.

Non blocking non blocking refers to the direct return of IO operations regardless of whether they are finished, and the corresponding operations continue to be processed in the background.

Tags: Java programming language

Posted by agge on Thu, 11 Aug 2022 01:55:55 +0930