java IO series IO model foundation details

Catalog

1. BIO(Blocking IO)

Application scenario:

1.2 NIO(Non Blocking IO)

Application scenario:

Conclusion:

3. AIO(NIO 2.0)

4. Comparison of bio, NIO and AIO:

Conclusion:

Java supports three kinds of network programming IO modes: BIO, NIO, AIO. The IO model is to use what kind of channel to send and receive data,

1. BIO(Blocking IO)

Synchronous blocking model, one client connection corresponds to one processing thread

Disadvantages:

1. Read operation in IO code is blocking operation. If the connection does not do data read and write operation, the thread will block and waste resources

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

Application scenario:

The BIO mode is suitable for a fixed architecture with a small number of connections. This mode requires high server resources, but the program is simple and easy to understand.

BIO code example: https://github.com/ssy-githup/io-mode Under the bio package of

//Server code:
/**
 * bio Server in mode
 */
public class SocketServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(9000);
        while (true) {
            System.out.println("Waiting to connect..");
            //Blocking method
            final Socket socket = serverSocket.accept();
            System.out.println("There is a client connected..");
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        handler(socket);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            //handler(socket);

        }
    }
    private static void handler(Socket socket) throws IOException {
        System.out.println("Current thread ID= " + Thread.currentThread().getId());
        byte[] bytes = new byte[1024];

        System.out.println("Get ready read. . ");
        //Receive client data, block method, block when no data is 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("Current thread ID = " + Thread.currentThread().getId());

        }
        socket.getOutputStream().write("HelloClient".getBytes());
        socket.getOutputStream().flush();
    }
}

Client code:

/**
 * bio Client
 */
public class SocketClient {

    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 9000);
        //Send data to server
        socket.getOutputStream().write("HelloServer".getBytes());
        socket.getOutputStream().flush();
        System.out.println("End of sending data to 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();
    }
}

1.2 NIO(Non Blocking IO)

Synchronization is non blocking. The server implementation mode is that one thread can handle multiple requests (connections). The connection requests sent by the client will be registered on the multiplexer selector, and the multiplexer will process when it polls the connection for IO requests. The Linux API (select, poll, epoll) is generally used to implement the I/O multiplexing bottom layer. Their differences are as follows:

  select poll epoll(jdk 1.5 and above)

Operation mode

ergodic

ergodic

Callback

Bottom implementation

array

Linked list

Hashtable

IO efficiency

Each call is traversed linearly with a time complexity of O(n)

Each call is traversed linearly with a time complexity of O(n)

Event notification mode: whenever an IO event is ready, the callback function registered by the system will be called, time complexity O(1)

Maximum connection

Upper limit

No upper limit

No upper limit

Application scenario:

NIO mode is applicable to the architecture with a large number of connections and a short connection (light operation), such as chat server, bullet screen system, communication between servers, complex programming, JDK 1.4 started to support

NIO has three core components: channel, buffer and selector

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

2. The channel will be registered on the selector, and the selector will hand it over to an idle thread according to the occurrence of channel read-write events

3. selector can correspond to one or more threads

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

NIO code example: server code

public class NIOServer {


    public static void main(String[] args) throws IOException {
        // Create a service Socket channel to listen on the local port and set it to non blocking mode
        ServerSocketChannel ssc = ServerSocketChannel.open();
        //It must be configured as non blocking to register on the selector, otherwise an error will be reported. The selector mode itself is non blocking mode
        ssc.configureBlocking(false);
        ssc.socket().bind(new InetSocketAddress(8888));
        // 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);

        while (true) {
            System.out.println("Wait for the event to occur..");
            // Polling the key in the monitoring channel, select is blocked, and accept() is also blocked
            int select = selector.select();

            System.out.println("Something happened..");
            // There are client requests that are polled and monitored
            Iterator<SelectionKey> it = selector.selectedKeys().iterator();
            while (it.hasNext()) {
                SelectionKey key = it.next();
                //Delete the key processed this time to prevent the next select from being processed repeatedly
                it.remove();
                handle(key);
            }
        }
    }

    private static void handle(SelectionKey key) throws IOException {
        if (key.isAcceptable()) {
            System.out.println("A client connection event occurred..");
            ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
            //NIO non blocking embodiment: the accept method is blocked here, but because of the connection event, the method will be executed immediately and will not block
            //After processing the connection request, the client will not continue to wait for the data to be sent
            SocketChannel sc = ssc.accept();
            sc.configureBlocking(false);
            //Interested in reading events when listening to Channel through Selector
            sc.register(key.selector(), SelectionKey.OP_READ);
        } else if (key.isReadable()) {
            System.out.println("A client data readable event occurred..");
            SocketChannel sc = (SocketChannel) key.channel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            //NIO non blocking embodiment: first, read method will not block, second, this event response model, when the read method is called, there must be an event that the client sends data
            int len = sc.read(buffer);
            if (len != -1) {
                System.out.println("Read to data sent by 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, you need to cancel writing events when there is no data to write out,
            // Register to write events when data is written out
            key.interestOps(SelectionKey.OP_READ);
            //sc.close();
        }
    }
}

Client code:

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", 8888);
        client.connect();
    }

    /**
     * Obtain a Socket channel, and do some initialization work for the 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 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.op'connect event for the channel.
        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();
            // Get 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 duplicate processing
                it.remove();
                // Connection event occurred
                if (key.isConnectable()) {
                    SocketChannel channel = (SocketChannel) key.channel();
                    // If 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 of 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 {
        //Same as the read method of the server
        // Server readable message: get the Socket channel where the event occurred
        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));
        }
    }
}

NIO server code flow:

  1. Create a ServerSocketChannel and Selector, and register the ServerSocketChannel to the Selector
  2. The selector listens for channel events through the select() method. When the client connects, the selector listens for connection events and gets the selectionKey bound when the ServerSocketChannel is registered
  3. selectionKey can get the bound ServerSocketChannel through the channel() method
  4. ServerSocketChannel get SocketChannel through accept() method
  5. Register the SocketChannel to the Selector and care about the read event
  6. After registration, a SelectionKey will be returned, which will be 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 for the read event and gets the selectionKey bound when SocketChannel registers
  8. selectionKey can get 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

Conclusion:

The NIO model's selector is like a general manager, which is responsible for listening to various IO events and then transferring them to the back-end thread for processing. NIO is not blocked relative to BIO. The back-end thread of BIO needs to block and wait for the client to write data (such as read method). If the client does not write data, the thread will block. NIO gives the task of waiting for the client's operation to the big manager, selector It is responsible for polling all registered clients. Only when an event occurs can it be transferred 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 client event data. After processing, it will end immediately, or return to the thread pool for other client events to continue to use. And the channel read and write are non blocking

3. AIO(NIO 2.0)

Asynchronous and non blocking. After the operating system completes, the callback informs the server program to start the thread to process. 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 a long connection (reoperation). JDK7 starts to support

 

4. Comparison of bio, NIO and AIO:

  BIO NIO AIO
IO model Synchronous block Synchronous non blocking Asynchronous non blocking
Code difficult simple complex complex
reliability difference good good
throughput low high high

Conclusion:

Use the fishing scene to compare the io model:

BIO:

Pseudo asynchronous IO: I

NIO:

AIO:

 

 

Published 65 original articles, won praise 3, visited 9043
Private letter follow

Keywords: socket Programming Java JDK

Added by padanaram on Sun, 08 Mar 2020 13:21:47 +0200