A review,

We learned the basic knowledge of NIO programming, and through a small demo to help understand the concept of NIO programming channel, buffer and so on. This article will continue learning about JAVA NIO programming with a small example to help you understand what you’re learning

  • Scatter and Scatter buffers
  • SocketChannel and ServerSocketChannel usage
  • The use of selectors

What is Scatter and Gather?

  • Reading from a Channel is when the data is read and written to multiple buffers. Therefore, a Channel “scatters” the data read from a Channel into multiple buffers.
  • To gather data into a Channel means to write data from multiple buffers to the same Channel during a write operation. Therefore, a Channel “gathers” data from multiple buffers and sends it to a Channel.

Scatter diagram

The buffer must be filled from the channel before the following buffer is filled, which means that you cannot dynamically adjust the size of each buffer.

The sketch of the Gather

Aggregation and dispersion are opposite forms. When data is written from buffer to channel, only the positon position of buffer to limit position is written, which means that content can be dynamically written into channel.

Three, selector

What is a selector

A Selector is a component in Java NIO that can detect multiple NIO channels and know if the channel is ready for events such as read and write events. In this way, a single thread can manage multiple channels, thus managing multiple network connections and improving efficiency.

Why use a selector

Using a selector allows you to manage multiple channels with a single thread. If multiple channels are managed by multiple threads, switching between threads consumes resources, while a single thread avoids switching between threads.

Common methods of selectors

The method name function
register(Selector sel, int ops) Register channels with the selector, and can choose to register the specified events, currently divided into four types; 1.Connect, 2.Accept, 3.Read, 4
select() Block until at least one channel is ready on the event you registered
selectNow() Will not block, whatever channel is ready to return immediately
select(long timeout) Same as select(), except that it blocks at most timeout milliseconds.
selectedKeys() Once the select() method is called and the return value indicates that one or more channels are ready, the ready channels in the selectedKey Set can be accessed by calling the Selector selectedKeys() method
wakeUp() You can make an object blocked by calling SELECT () return without blocking.
close() Calling its close() method after a Selector is used closes that Selector and invalidates any SelectionKey instances registered with that Selector. The channel itself does not close

Four, in actual combat

Actual combat requirement description

Encoding client and server, the server can accept the client’s request, and return a message, the client receives the message and parses the output.

Server code

Try {// Create a server socket and open ServerSocketChannel ServerSocketChannel = ServerSocketChannel.open(); / / to monitor serverSocketChannel binding port 8090. The socket (). The bind (new InetSocketAddress (8090)); / / set to non-blocking mode serverSocketChannel. ConfigureBlocking (false);
            while(true) {/ / get request connected SocketChannel SocketChannel = serverSocketChannel. The accept ();if(socketChannel! =null){ ByteBuffer buf1 = ByteBuffer.allocate(1024); socketChannel.read(buf1); buf1.flip();if(buf1.hasRemaining())
                        System.out.println(">>> Server receives data:"+new String(buf1.array())); buf1.clear(); // Construct the returned packet, which is divided into header and body. In actual situations, complex packet protocols can be constructed. ByteBuffer header = ByteBuffer.allocate(6); header.put("[head]".getBytes());
                    ByteBuffer body   = ByteBuffer.allocate(1024);
                    body.put("i am body!".getBytes());
                    header.flip();
                    body.flip();
                    ByteBuffer[] bufferArray = { header, body };
                    socketChannel.write(bufferArray);

                    socketChannel.close();
                }else{
                    Thread.sleep(1000);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
Copy the code

Server side selector(selector version)

Try {// open Selector = Selector. Open (); ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.socket().bind(new InetSocketAddress(8090)); serverSocketChannel.configureBlocking(false); / / registered selector with the channel, and registered to accept events serverSocketChannel. Register (selector, SelectionKey. OP_ACCEPT);while (trueInt readyChannels = selectNow(); // If not, try againif (readyChannels == 0) continue; // Get the Set of events in the prepared channel Set selectedKeys = selectedKeys(); Iterator keyIterator = selectedKeys.iterator();while (keyIterator.hasNext()) {
                    SelectionKey key = (SelectionKey) keyIterator.next();
                    if(key.isacceptable ()) {// Write the business logic in the event you register, // I register the accept event, // This part of the logic is the same as the non-selector server code above. ServerSocketChannel serverSocketChannel1 = (ServerSocketChannel) key.channel(); SocketChannel socketChannel = serverSocketChannel1.accept(); ByteBuffer buf1 = ByteBuffer.allocate(1024); socketChannel.read(buf1); buf1.flip();if (buf1.hasRemaining())
                            System.out.println(">>> Server receives data:" + new String(buf1.array()));
                        buf1.clear();

                        ByteBuffer header = ByteBuffer.allocate(6);
                        header.put("[head]".getBytes());
                        ByteBuffer body = ByteBuffer.allocate(1024);
                        body.put("i am body!".getBytes());
                        header.flip();
                        body.flip();
                        ByteBuffer[] bufferArray = {header, body};
                        socketChannel.write(bufferArray);

                        socketChannel.close();
                    } else if (key.isConnectable()) {
                    } else if (key.isReadable()) {
                    } else if(key.iswritable ()) {} // Note the keyiterator.remove () call at the end of each iteration. //Selector does not remove the SelectionKey instance from the selected key set itself. It must be removed by itself when the channel is processed. // The next time the channel becomes ready, the Selector puts it in the selected keysetkeyiterator.remove () again; } } } catch (IOException e) { e.printStackTrace(); }Copy the code

Client code

SocketChannel SocketChannel = socketchannel.open (); SocketChannel = socketchannel.open (); socketChannel.connect(new InetSocketAddress("127.0.0.1", 8090)); // Request server, send request ByteBuffer buf1 = ByteBuffer. Allocate (1024); buf1.put("Request from the client.".getBytes());
            buf1.flip();
            if(buf1.hasRemaining()) socketChannel.write(buf1); buf1.clear(); We define the first 6 bytes as the header and the following other bytes as the body content. ByteBuffer header = ByteBuffer.allocate(6); ByteBuffer body = ByteBuffer.allocate(1024); ByteBuffer[] bufferArray = { header, body }; socketChannel.read(bufferArray); header.flip(); body.flip();if (header.hasRemaining())
                System.out.println(">>> Client receives header data:" + new String(header.array()));
            if (body.hasRemaining())
                System.out.println(">>> Client receives body data:" + new String(body.array()));
            header.clear();
            body.clear();


            socketChannel.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
Copy the code

The results

Server:

Client:

Two versions of the server code are shown here, a non-selector version and a selector version. The final running result shows that the client correctly parses the header and body contents according to the protocol format agreed by both parties. In fact, this is the main function and application scenario of aggregation and dispersion. In network interaction, the format of protocol packets is defined and implemented. After learning the introduction to NIO programming, we finally carried out the summative actual combat, wrote a RPC demo framework to realize the remote call of distributed system. Interested students can pay attention to the author and subsequent articles.

reference

The JAVA NIO

Recommended reading

Java Lock ReentrantLock (part 1)

Java Lock ReentrantLock (part 2)

Java Lock ReentrantReadWriteLock

Introduction to JAVA NIO Programming (PART 1)