“This is the 15th day of my participation in the First Challenge 2022. For details: First Challenge 2022.”

The Selector profile

1. Selector and Channel relationship

A Selector is generally called a Selector and can be translated as multiplexing. It is one of the Java NIO core components that checks whether the state of one or more NIO channels is readable and writable. In this way, a single thread can manage multiple Channels, that is, multiple network links can be managed.

The advantage of using Selector is that fewer threads can be used to process the channel, avoiding the overhead of thread context switching compared to using multiple threads.

2. SelectableChannel

(1) Not all channels can be multiplexed by Selector. For example, FileChannel cannot be reused by selectors. To determine whether a Channel can be reused by Selector, one prerequisite is to determine whether it inherits an abstract class, SelectableChannel. SelectableChannel can be reused if it is inherited, but not otherwise.

(2) SelectableChannel provides the common methods needed to implement channel selectivity. It is the parent of all ready-to-check channel classes. All socket channels, which inherit from the SelectableChannel class, are optional, including channels fetched from the Pipe object. The FileChannel class, however, does not inherit SelectableChannel and is therefore not an optional channel.

(3) A channel can be registered with more than one selector, but only once for each selector. The relationship between channels and selectors is done using registration. SelectableChannel can be registered with a Selector object, and at the time of registration, those operations that need to be specified for the channel, are of interest to the Selector.

3. A Channel registers with a Selector

(1) Use channel. register(Selector sel, int pos) method to register a Channel to a Selector. The first parameter specifies the selector to register for the channel. The second parameter specifies the channel operations that the selector needs to query.

(2) Channel operations that can be queried by the selector can be classified into the following four types:

  • Can read: SelectionKey OP_READ
  • Write: SelectionKey OP_WRITE
  • Connection: SelectionKey OP_CONNECT
  • Receive: SelectionKey OP_ACCEPT

If Selector is interested in the multi-operation type of a channel, it can be done with the bit-or operator:

Such as int key = SelectionKey. OP_READ | SelectionKey. OP_WRITE;

(3) The selector queries not the operation of the channel, but a ready state of an operation of the channel. The ready state of what operation? Once a channel has the condition to complete an operation, it means that an operation of the channel is ready and can be queried by Selector, and the program can perform the corresponding operation on the channel. For example, if a SocketChannel can connect to a server, it is in the Connection-ready state (OP_CONNECT). For another example, if a ServerSocketChannel server channel is ready to receive incoming connections, it is in the OP_ACCEPT state. For example, a data readable channel can be said to be OP_READ. A channel waiting to write data is said to be OP_WRITE.

4. Select Key

(1) After a Channel is registered, and once the Channel is in some ready state, it can be queried by the selector. This is done using the select() method of the Selector. The select method is used to query the ready state of the channel operation of interest.

(2) Selector can continuously query the ready state of operations occurring in a Channel. And select the ready state to go. Once a channel has a ready state for an operation, and it’s an operation that Selecor is interested in, it gets selected by Selector and goes into the selection key set.

(3) A Selector key that first contains the type of the Selector channel operation registered in the Selector, for example: selectionKey.op_read. It also contains registration relationships between specific channels and specific selectors.

The development of applications is that the select key is the key to programming, NIO programming, is more corresponding to the select key, different business logic processing.

(4) The concept of select key is similar to the concept of event. A select key is like an event in listener mode. Because Selector is not an event-triggered pattern, it’s an actively queried pattern, it’s not called an Event, it’s called a SelectionKey to select a key.

How to use Selector

1. Selector creation

Create a Selector object using the Selector. Open () method. The following;

Selector = Selector. Open ();Copy the code

2. Register a Channel with Selector

To implement a Selector to manage a Channel, you need to register the Channel with the corresponding Selector

Selector = Selector. Open (); ServerSocketChannel socketChannel = serverSocketChannel.open (); / / 3. Set to not block the socketChannel. ConfigureBlocking (false); Socketchannel.bind (new InetSocketAddress(9999)); Socketchannel. register(selector, selectionkey.op_accept);Copy the code

The channel’s register() method above registers it with a selector.

Note that:

(1) with the Selector is used, the channel must be in non-blocking mode, otherwise will throw an exception IllegalBlockingModeException. This means that FileChannel cannot be used with a Selector because FileChannel cannot switch to non-blocking mode, as can all channels associated with a socket.

(2) A channel does not have to hold all four operations. For example, ServerSocketChannel supports Accept, whereas SocketChannel client does not. You can retrieve the set of operations supported under a particular channel through the vildOps() method on the channel.

3. The rotation training query is ready for operation

(1) Ready channel operations can be queried through the Selector select() method. Some ready state sets are contained in a Set whose elements are Selectionkey objects

(2) Here are a few overloaded query select() methods:

  • Select () blocks until at least one channel is ready on the event you registered.
  • Select (long timeout) is the same as select(), but the longest blocking event is timeout milliseconds.
  • SelectNow () is non-blocking and returns as soon as there is a channel.

The int returned by the select() method, indicating how many channels are ready, specifically the current select

Method goes to how many channels are programmed in a ready state between the select methods.

For example, the first call to select() returns 1 if one channel is programmed to be ready, and the next call to select() returns 1 if another channel is ready. If the first ready chnanel does nothing, there are now two ready channels, but only one is ready between each select() method call.

Once the select() method is called and returns position 0, there is a seletedKeys() method in Selector that ranges the set of selected keys, iterates over each element of the set, and performs the corresponding action based on the type of ready action

Set<SelectionKey> selectedKeys = selectedKeys(); Iterator<SelectionKey> iterator = selectedKeys.iterator(); while (iterator.hasNext()) { SelectionKey key = iterator.next(); IsAcceptable () {// a connection was accepted by a ServerSocketChannel.} else if (key.isacceptable ()) {// a connection was accepted by a ServerSocketChannel (key.isConnectable()) { // a connection was established with a remote server. } else if (key.isReadable()) { // a channel is ready for reading } else if (key.isWritable()) { // a channel is ready for writing } } iterator.remove();Copy the code

4. Stop selecting methods

The selector performs a summary of the selection process, and the underlying system asks each channel in turn if it is ready. This process may cause the calling thread to block, so there are three ways to wake up a blocked thread in the select () method.

Wakeup () method: Causes the blocked select() method to return immediately by calling the Selector object’s wakeup() method

This method causes the first hash on the selector that did not return to be returned immediately. If no selection operation is currently in progress, the next call to the select() method returns immediately.

Close () method: Turns off the selector with the close() method

This method wakes up any thread that blocks during the selection (similar to wakeup()), and causes all channels registered with the Selector to be unregistered. All keys are cancelled, but the Channel itself is not closed.

The sample code

1. Server-side code

@Test public void server() throws IOException { //1. ServerSocketChannel ServerSocketChannel = ServerSocketChannel.open(); / / 2. Switch the non-blocking mode serverSocketChannel. ConfigureBlocking (false); ByteBuffer readBuffer = ByteBuffer. Allocate (1024); ByteBuffer writeBuffer = ByteBuffer.allocate(1024); Writebuffer. put(" Received..." .getBytes(StandardCharsets.UTF_8)); Serverketchannel. bind(new InetSocketAddress(20000)); Selector = selector. Open (); / / 6. Channel registered with the selector, monitor serverSocketChannel. Register (selector, SelectionKey. OP_ACCEPT); While (SelectionKey () > 0) {Set<SelectionKey> selectionKeys = selection.selectedKeys (); Iterator<SelectionKey> selectionKeyIterator = selectionKeys.iterator(); / / cycle while (selectionKeyIterator hasNext ()) {/ / get ready state SelectionKey k = selectionKeyIterator. The next (); / / operation to judge the if (k.i sAcceptable ()) {/ / to get connected SocketChannel accept = serverSocketChannel. The accept (); Accept.configureblocking (false); Accept. register(selector, selectionkey.op_read); } else if (k.isReadable()) { SocketChannel socketChannel = (SocketChannel) k.channel(); readBuffer.clear(); socketChannel.read(readBuffer); readBuffer.flip(); System.out.println("received:" + new String(readBuffer.array(), StandardCharsets.UTF_8)); k.interestOps(SelectionKey.OP_WRITE); } else if (k.isWritable()) { writeBuffer.rewind(); SocketChannel socketChannel = (SocketChannel) k.channel(); socketChannel.write(writeBuffer); k.interestOps(SelectionKey.OP_READ); }}}}Copy the code

2. Client code

@Test public void client() throws IOException { //1. SocketChannel SocketChannel = SocketChannel.open(new InetSocketAddress(20000)); / / 2. The switch to a non-blocking mode socketChannel. ConfigureBlocking (false); ByteBuffer = ByteBuffer. Allocate (1024); Buffer.put (new Date().toString().getBytes(StandardCharsets.utf_8)); //5. Mode switch buffer.flip(); Socketchannel. write(buffer); //6. / / 7. Close the buffer. The clear (); socketChannel.close(); }Copy the code

3. Summary of NIO programming steps

1. Create a ServerSocketChannel

2. Set to non-blocking mode

3. Create a Selector

4. A Channel registers with a selector to listen for connection events

5. Call the select method in Selector (loop call) and listen to see if the channel is ready

6. Call SelectKeys() to get the collection of ready channels

7. Traverse the set of ready channels, judge the type of ready events, and realize specific business operations.

8. According to the business process, determine whether to register event listening events again and repeat the execution.