Chapter 3 - Java NIO programming: Selector selector

Posted by Shad on Sun, 06 Mar 2022 03:24:23 +0100

1, Introduction to Selector

(1) NIO of Java uses non blocking IO mode. You can use one thread to process multiple client connections, and you will use the selector.

(2) The Selector can detect whether events occur on multiple registered channels (Note: multiple channels can be registered to the same Selector in the form of events). If an event occurs, it will obtain the event and then process each event accordingly. In this way, only one single thread can manage multiple channels, that is, manage multiple connections and requests.

(3) Only when there is a real read-write event in the connection / channel, the read-write will be carried out, which greatly reduces the system overhead, and there is no need to create a thread for each connection and maintain multiple threads.

(4) It avoids the overhead caused by context switching between multiple threads.

2, Schematic diagram and feature description of Selector

Re description of features:

(1) Netty's IO thread NioEventLoop aggregates selectors (selectors, also known as multiplexers), which can handle hundreds of client connections concurrently.

(2) When a thread reads and writes data from a client Socket channel, if no data is available, the thread can perform other tasks.

(3) Threads usually use the idle time of non blocking IO to perform IO operations on other channels, so a single thread can manage multiple input and output channels.

(4) Since read and write operations are non blocking, this can fully improve the operation efficiency of IO threads and avoid thread suspension caused by frequent I/O blocking.

(5) One I/O thread can handle N client connections and read-write operations concurrently, which fundamentally solves the traditional synchronous blocking I/O connection thread model, and greatly improves the performance, flexibility and reliability of the architecture.

3, Selector class related methods

Selector class is an abstract class. Common methods and descriptions are as follows:

public abstract class Selector implements Closeable {
  //Get a selector object
  public static Selector open();
  
  //Determine whether this selector is on
  public abstract boolean isOpen()
  
  //Returns the provider who created this channel
  public abstract SelectorProvider provider()
  
  //Returns the keyset of this selector
  public abstract Set<SelectionKey> keys()
  
  //Returns the selected key set of this selector and gets all the selectionkeys from the internal set
  public abstract Set<SelectionKey> selectedKeys();
  
  //Return immediately without blocking, select a group of keys, and the corresponding channel is ready for IO operation
  public abstract int selectNow() throws IOException;
  
  //Monitor all registered channels. When there are IO operations available, add the corresponding SelectionKey to the internal set and return,
  //Parameter is used to set the timeout, that is, blocking for a certain millisecond and returning after the specified millisecond
  public abstract int select(long timeout) throws IOException;
  
  //Select a set of keys whose corresponding channel is ready for IO} operation
  //This method performs a blocking selection operation
  public abstract int select() throws IOException;
  
  //Wake up selector
  public abstract Selector wakeup();
  
  //Close this selector
  public abstract void close() throws IOException;
}

Note: the ServerSocketChannel function in NIO is similar to ServerSocket, and the SocketChannel function is similar to Socket

3, Schematic diagram of NIO non blocking network programming

NIO non blocking network programming related (Selector, SelectionKey, ServerScoketChannel and SocketChannel) relationship diagram

explain:

  1. When there is a client connection, the SocketChannel will be obtained through ServerSocketChannel
  1. The Selector listens to the select method and returns the number of channels with events

   3. Register the socketchannel to the selector through the register(Selector sel, int ops) method. Multiple socketchannels can be registered on a selector

  1. After registration, a SelectionKey will be returned, which will be associated with the Selector (Collection)
  1. Further obtain each SelectionKey (event occurs)
  1. Then get the SocketChannel in reverse through the channel() method of SelectionKey
  1. Business processing can be completed through the obtained channel

4, NIO non blocking network programming quick start

Case requirements:

  1. Write a NIO entry case to realize simple data communication between server and client (non blocking)
  2. Objective: to understand NIO non blocking network programming mechanism

Server side:

public class NIOServer {
    public static void main(String[] args) throws Exception{

        //Create ServerSocketChannel - > ServerSocket
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        //Get a Selector object
        Selector selector = Selector.open();

        //Bind a port 6666 and listen on the server side
        serverSocketChannel.socket().bind(new InetSocketAddress(6666));
        //Set to non blocking
        serverSocketChannel.configureBlocking(false);

        //Register the "serverSocketChannel" with the "selector" event as "op"_ ACCEPT
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("Registered selectionkey quantity=" + selector.keys().size());


        //Cycle waiting for client connection
        while (true) {
            //Here, we wait for 1 second. If no event occurs, we return
            //No events occurred
            if (selector.select(1000) == 0) {
                System.out.println("The server waited for 1 second and there was no connection");
                continue;
            }

            //If the returned is > 0, the relevant selectionKey collection is obtained
            //1. If the returned > 0, it indicates that the event of interest has been obtained
            //2. selector.selectedKeys() returns a collection of events of interest
            //Reverse channel acquisition through {selectionKeys}
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            System.out.println("selectionKeys quantity = " + selectionKeys.size());

            //Traverse set < selectionkey >, and use iterator to traverse
            Iterator<SelectionKey> keyIterator = selectionKeys.iterator();

            while (keyIterator.hasNext()) {
                //Get SelectionKey
                SelectionKey key = keyIterator.next();

                //Handle the events according to the key # corresponding channel
                //If it's OP_ACCEPT, there is a new client connection
                if (key.isAcceptable()) {
                    //Generate a} SocketChannel for the client
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    System.out.println("Client connection succeeded Generated a socketChannel " + socketChannel.hashCode());

                    //Set # SocketChannel # to non blocking
                    socketChannel.configureBlocking(false);

                    //Register the socketChannel , with the selector, and the attention event is , OP_READ,
                    //At the same time, a Buffer is associated with the socket channel
                    socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));

                    System.out.println("After client connection ,Registered selectionkey quantity=" + selector.keys().size());
                }

                //Occurrence of OP_READ
                if (key.isReadable()) {

                    //Get the corresponding channel through key # reverse
                    SocketChannel socketChannel = (SocketChannel)key.channel();
                    System.out.println("From socketChannel Read data from:" + socketChannel.hashCode());
                    //Get the buffer associated with the channel
                    ByteBuffer buffer = (ByteBuffer) key.attachment();
                    socketChannel.read(buffer);
                    System.out.println("form client " + new String(buffer.array()));
                }

                //Manually move the current selectionKey from the collection to prevent repeated operations
                keyIterator.remove();
            }
        }
    }
}

client:

public class NIOClient {

    public static void main(String[] args) throws Exception{
        //Get a network channel
        SocketChannel socketChannel = SocketChannel.open();
        //Set non blocking
        socketChannel.configureBlocking(false);
        //Provide ip # and ip # ports on the server side
        InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1"6666);

        //Connect server
        if (!socketChannel.connect(inetSocketAddress)) {

            while (!socketChannel.finishConnect()) {
                System.out.println("Because the connection takes time, the client will not block and can do other work..");
            }
        }

        //If the connection is successful, send the data
        String str = "Hello,China~~~";
        //Wraps a byte array into a buffer
        ByteBuffer buffer = ByteBuffer.wrap(str.getBytes());
        //Send data and write the # buffer to # channel
        socketChannel.write(buffer);
        System.in.read();

    }
}

Tips: this part of the code may not be easy to understand. It will be easier to understand if you Debug it yourself.  

 

Topics: Netty