[NIO multiplexer]

Article directory

  • Summarize:
    • Three cores of NIO
      • Buffer buffer
      • Channel
      • Selector selector
    • Comparison of NIO and BIO
    • Multiplexer/Selector (Selector)
    • Analysis of NIO non-blocking network communication principle
    • Implementation process
    • summary

Summary:

The three core parts of NIO are buffer, channel and multiplexer. It is an improvement for bio. The disadvantage of bio is that one client connection corresponds to one server thread, and the overhead is high. nio implements a thread to manage multiple threads. A client connection, in which the multiplexer is the core, the multiplexer can monitor events of multiple channels (such as: connection request, data arrival, etc.), so a single thread can monitor multiple client channels, When the channel triggers the ready event, some operations will be performed on the channel, which also achieves non-blocking.

Selector is a Java NIO component that can inspect one or more NIO channels and determine which channels are ready for reading or writing. In this way, a single thread can manage multiple channels, thereby managing multiple network connections and improving efficiency

Three cores of NIO

Buffer

A buffer is essentially a block of memory that data can be written to and then read from. This piece of memory is packaged as a NIO Buffer object, and a set of methods are provided for easy access to this piece of memory. Compared with direct operations on arrays, the Buffer API is easier to operate and manage.

Channel

The channel of Java NIO is similar to the stream, but there are some differences: data can be read from the channel, and data can be written to the channel. But stream (input or output) reading and writing are usually one-way. Channels can be read and written non-blockingly, and channels can support reading or writing buffers, as well as asynchronous reading and writing.

Selector

Selector is a Java NIO component that can inspect one or more NIO channels and determine which channels are ready for reading or writing. In this way, a single thread can manage multiple channels, thereby managing multiple network connections and improving efficiency

  • Each channel will correspond to a Buffer
  • One thread corresponds to Selector, and one Selector corresponds to multiple channels (connections)
  • Which channel the program switches to is determined by the event
  • Selector will switch on each channel according to different events
  • Buffer is a memory block, and the bottom layer is an array
  • The reading and writing of data is done through Buffer. The BIO is either an input stream or an output stream, which cannot be bidirectional, but NIO’s Buffer can be read or written.
  • Channel is responsible for transmission, and Buffer is responsible for accessing data

Comparison of NIO and BIO

  • BIO processes data in a streaming manner, while NIO processes data in a block manner, and the efficiency of block I/O is much higher than that of stream I/O
  • BIO is blocking, NIO is non-blocking
  • BIO operates based on byte stream and character stream, while NIO operates based on Channel (channel) and Buffer (buffer), and data is always from channel
    Read into a buffer, or write from a buffer into a channel. Selector (selector) is used to monitor events of multiple channels (for example: connection request, data arrival, etc.), so multiple client channels can be monitored using a single thread

Multiplexer/Selector (Selector)

Selector (Selector) is a multiplexer of SelectableChannle objects. Selector can monitor the IO status of multiple SelectableChannels at the same time. That is to say, using Selector can make a single thread manage multiple Channels. Selector is the core of non-blocking IO.

  • Java’s NIO uses non-blocking IO. You can use one thread to handle multiple client connections, and you will use Selector (selector)
  • Selector can detect whether there are events on multiple registered channels (note: multiple Channels can be registered to the same
    Selector), if an event occurs, it will get the event and then process each event accordingly. In this way, only one single thread can be used to manage
    Manage multiple channels, that is, manage multiple connections and requests.
  • Only when the connection/channel actually has read and write events, will it read and write, which greatly reduces the system overhead, and does not need to create a thread for each connection, does not need to maintain multiple threads, and avoids multi-threading. The overhead caused by context switching between.

NIO non-blocking network communication principle analysis

Selector can achieve: One I/O thread can concurrently process N client connections and read and write operations, which fundamentally solves the traditional synchronous blocking I/O one connection one thread model, architecture performance, elastic scalability and reliability have been greatly improved.

Execution process

Server: Obtain the channel (this channel is similar to the stream in BIO mode, which is responsible for establishing a connection with the client, including specifying the connection port number), obtain the multiplexer, and The server channel is registered on the selector, and begins to specify to listen to receive events (listen to the client), and then the multiplexer polls the ready event (for the client ready event), after entering this event, use the iterator to get All registered channel ready events in the multiplexer (for the channel ready event corresponding to the client ready event), and then judge the channel ready event obtained by the iterator, the first round is the access ready event (the first In the case of the round, it is access, because no client is connected to the server channel at this time), so at this time, the channel of the client should be obtained and the client should be registered on the selector. In addition to the access event, it may also be a read event (because It is the server, which reads the data of the client), at this time, it should get the read ready event on the multiplexer and read the data, (because it is NIO non-blocking when reading, it is based on the buffer, read data into the buffer)

/**
 Server
 */
public class Server {<!-- -->
    public static void main(String[] args) throws IOException {<!-- -->
        //1. Get channel
        ServerSocketChannel ssChannel = ServerSocketChannel. open();
        //2. Switch to non-blocking mode
        ssChannel. configureBlocking(false);
        //3. Bind connection
        ssChannel.bind(new InetSocketAddress(9999));
        //4. Get the selector
        Selector selector = Selector. open();
        //5. Register the channel to the selector, and specify "listen to receive events"
        ssChannel.register(selector, SelectionKey.OP_ACCEPT);
        //6. Polling to get "ready" events on the selector
        while (selector. select() > 0) {<!-- -->
            System.out.println("round round");
            //7. Get all registered "selection keys (ready listener events)" in the current selector
            Iterator<SelectionKey> it = selector.selectedKeys().iterator();
            while (it.hasNext()) {<!-- -->
                //8. Get ready "ready" is the event
                SelectionKey sk = it. next();
                //9. Determine what specific event is ready
                if (sk.isAcceptable()) {<!-- -->
                    //10. If "ready to receive", get the client connection
                    SocketChannel sChannel = ssChannel. accept();
                    //11. Switch to non-blocking mode
                    sChannel. configureBlocking(false);
                    //12. Register the channel to the selector
                    sChannel.register(selector, SelectionKey.OP_READ);
                } else if (sk.isReadable()) {<!-- -->
                    //13. Get the channel in the "ready" state on the current selector
                    SocketChannel sChannel = (SocketChannel) sk. channel();
                    //14. Read data
                    ByteBuffer buf = ByteBuffer. allocate(1024);
                    int len = 0;
                    while ((len = sChannel. read(buf)) > 0) {<!-- -->
                        buf. flip();
                        System.out.println(new String(buf.array(), 0, len));
                        buf. clear();
                    }
                }
                //15. Cancel selection key SelectionKey
                it. remove();
            }
        }
    }
}

Client: First obtain the channel, configure the port, and then specify the buffer (because in NIIO, the channel is only responsible for the connection, and the buffer is responsible for the temporary storage of data), and connect the client data through the channel The buffer is temporarily stored and sent to the server.

/**
  client
 */
public class Client {<!-- -->

public static void main(String[] args) throws Exception {<!-- -->
//1. Get channel
SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9999));
//2. Switch to non-blocking mode
sChannel. configureBlocking(false);
//3. Allocate a buffer of the specified size
ByteBuffer buf = ByteBuffer. allocate(1024);
//4. Send data to the server
Scanner scan = new Scanner(System.in);
while(scan.hasNext()){<!-- -->
String str = scan. nextLine();
buf.put((new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(System.currentTimeMillis())
+ "\
" + str).getBytes());
buf. flip();
sChannel.write(buf);
buf. clear();
}
//5. Close the channel
sChannel. close();
}
}

Summary

Summary of BIO, NIO, AIO

  • Bio is synchronous and blocked, that is to say, one connection is one thread, and it is blocked. When the client has no data, the server thread also needs to wait (after the connection is established, it waits for the client’s data, waits all the time, and the thread is always used), unnecessary Thread overhead, we can also use the thread pool to improve this architecture, but there are still limitations when the thread is large.
  • Nio is synchronous and non-blocking, SocketChannel and ServerSocketChannel, one request one thread, one thread maintains a multiplexer, registers the client’s channel requests to this multiplexer in the form of events, and the multiplexer to train the channel ( epoll array), when the channel has data, it will be processed, and the rotation training is the responsibility of the interrupt program of the system. The read method of the socket is blocked, but my thread is training in rotation to process events, not waiting for data and writing data all the time. It’s like an equal sign broadcast to notify you.
    Nio is actually reactor responsive programming. Because a certain operation is delayed and does not want to block, it returns immediately. When the processing is completed, it will notify and call the callback function. event-based.
  • Aio is asynchronous and non-blocking. One effective request is one thread. Asynchronous means that for example, the read method does not require us to use threads to read all the time, but the operating system to help us handle it and notify us to process it. The operating system is responsible for writing data and preparing data for reading. passed to the buffer.