I. Introduction to Selector
Selector Commonly referred to as selectors , Of course, you can also translate it into Multiplexer . It is one of the core components of Java NIO. It is used to check whether the state of one or more NiO channels is readable and writable. In this way, multiple channels can be managed by a single thread, that is, multiple network links can be managed.
The benefits of using Selector are: Using fewer threads to process channels avoids the overhead of thread context switching compared with using multiple threads.
II. Introduction to the use of Selector
1. Create selector
Create a Selector object by calling the Selector.open() method, as follows:
Selector selector = Selector.open();
It needs to be explained here
2. Register the Channel Selector
channel.configureBlocking(false); SelectionKey key = channel.register(selector, Selectionkey.OP_READ);
The Channel must be non blocking.
Therefore, FileChannel is not applicable to Selector because FileChannel cannot be switched to non blocking mode. More accurately, FileChannel does not inherit SelectableChannel. The Socket channel can be used normally.
SelectableChannel abstract class There is one configureBlocking() Method is used to put the channel in blocking mode or non blocking mode.
abstract SelectableChannel configureBlocking(boolean block)
be careful:
configureBlocking() of the SelectableChannel abstract class The method is by AbstractSelectableChannel is implemented by the abstract class. SocketChannel, ServerSocketChannel and DatagramChannel all inherit directly AbstractSelectableChannel abstract class .
If you are interested, you can take a look at NIO's source code, various abstract classes and abstract classes at the upper level of abstract classes. I am not going to study the NIO source code for the time being, because there are still many things to do. Students who need to study can have a look at it by themselves.
register() Method. This is a“ interest set ”, This means what events you are interested in when listening to the Channel through the Selector. You can listen to four different types of events:
- Connect
- Accept
- Read
- Write
A Channel triggers an event, which means that the event is ready. For example, a Channel successfully connects to another server“ Connection ready ”. When a Server Socket Channel is ready to receive a new incoming connection, it is called“ receive ready ”. A data readable Channel can be said to be“ Read ready ”. The Channel waiting to write data can be said to be“ Write ready ”.
These four events are represented by four constants of SelectionKey:
SelectionKey.OP_CONNECT SelectionKey.OP_ACCEPT SelectionKey.OP_READ SelectionKey.OP_WRITE
If you are interested in more than one event, use the or operator as follows:
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
3. Introduction to selectionkey
A SelectionKey key represents the registration relationship between a specific channel object and a specific selector object.
key.attachment(); //Returns the attachment of the SelectionKey, which can be specified when registering the channel. key.channel(); // Returns the channel corresponding to the SelectionKey. key.selector(); // Returns the Selector corresponding to the SelectionKey. key.interestOps(); //Returns the bit mask representing the IO operation to be monitored by the Selector key.readyOps(); // Returns a bit mask representing the IO operations that can be performed on the corresponding channel.
key.interestOps():
We can use the following methods to determine whether the Selector is interested in certain events in the Channel
int interestSet = selectionKey.interestOps(); boolean isInterestedInAccept = (interestSet & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT; boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT; boolean isInterestedInRead = interestSet & SelectionKey.OP_READ; boolean isInterestedInWrite = interestSet & SelectionKey.OP_WRITE;
key.readyOps()
The ready set is a collection of operations for which the channel is ready. The following methods are defined in JAVA to check whether these operations are ready
//Method for creating ready collection int readySet = selectionKey.readyOps(); //Methods to check whether these operations are ready key.isAcceptable();//Whether it is readable. If yes, return true boolean isWritable(): //Whether it is writable. If yes, return true boolean isConnectable(): //Whether it is connectable. If yes, return true boolean isAcceptable(): //Whether it is acceptable. If yes, return true
Accessing the Channel and Selector from the SelectionKey is simple as follows:
Channel channel = key.channel(); Selector selector = key.selector(); key.attachment();
You can attach an object or more information to the SelectionKey to easily identify a given channel. For example, you can attach a Buffer used with the channel or an object containing aggregate data. The usage method is as follows:
key.attach(theObject); Object attachedObj = key.attachment();
You can also attach objects when registering a Channel with the Selector using the register() method. For example:
SelectionKey key = channel.register(selector, SelectionKey.OP_READ, theObject);
4. Select channels from the Selector (selecting channels via Selector)
The selector maintains a set of registered channels, and this registration relationship is encapsulated in the SelectionKey
There are three types of SelectionKey collections maintained by the Selector:
-
Registered key set
The collection of keys generated by all channels associated with the selector is called the collection of registered keys. Not all registered keys are still valid. This collection passes through keys() Method and may be empty. This set of registered keys cannot be modified directly; attempting to do so will raise a java.lang.unsupported operationexception.
-
Selected key set
The collection of keys generated by all channels associated with the selector is called the collection of registered keys. Not all registered keys are still valid. This collection passes through keys() Method and may be empty. This set of registered keys cannot be modified directly; attempting to do so will raise a java.lang.unsupported operationexception.
-
Cancelled key set
A subset of the set of registered keys that contains cancel() Method has been called, but they have not been unregistered. This collection is a private member of the selector object and cannot be accessed directly.
be careful:
When the key is cancelled (via isValid()) Method, it will be placed in the set of cancelled keys of the relevant selector. The registration will not be cancelled immediately, but the key will be invalidated immediately. When called again select( ) Method (or at the end of an ongoing select() call), the cancelled keys in the set of cancelled keys will be cleared and the corresponding logoff will be completed. The channel will be logoff and the new SelectionKey will be returned. When the channel is closed, all related keys will be automatically cancelled (remember, a channel can be registered on multiple selectors) . when the selector is closed, all channels registered with the selector will be unregistered and the relevant key will be invalidated (cancelled). Once the key is invalidated, the selection related method calling it will throw canceledkeyexception.
Introduction to the select() method:
In the newly initialized Selector object, all three collections are empty. The ready channel can be selected through the select() method of the Selector (these channels contain events you are interested in). For example, if you are interested in read ready channels, the select() method will return those channels whose read events are ready. Here are several overloaded select() methods of Selector:
- int select(): blocking until at least one channel is ready for the event you registered.
- int select(long timeout): the same as select(), but the maximum blocking time is timeout milliseconds.
- int selectNow(): non blocking. It returns as soon as a channel is ready.
The int value returned by the select () method indicates how many channels are ready, and how many channels have become ready since the last call to the select () method. The channel that was ready in the previous select() call will not be recorded in this call, and the channel that was ready in the previous select() call but is no longer ready will not be recorded. For example, if the select () method is called for the first time, it will return 1 if one channel becomes ready. If the select () method is called again, it will return 1 again if another channel is ready. If nothing is done with the first ready channel, there are now two ready channels, but only one is ready between each select () method call.
3, Code example
Server
package com.tcc.test.nio.selector; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Set; /** * Copyright (C) @2021 * * @author: tcc * @version: 1.0 * @date: 2021/11/28 * @time: 18:45 * @description: */ public class WebServer { public static void main(String[] args) { try { ServerSocketChannel ssc = ServerSocketChannel.open();// Get one ServerSocket passageway ssc.socket().bind(new InetSocketAddress("127.0.0.1", 8000));//Binding listening ip And ports ssc.configureBlocking(false);//Non blocking Selector selector = Selector.open();//By calling Selector.open()Method to create a Selector object // register channel,And specify that the event of interest is Accept ssc.register(selector, SelectionKey.OP_ACCEPT); ByteBuffer readBuff = ByteBuffer.allocate(1024); ByteBuffer writeBuff = ByteBuffer.allocate(128); writeBuff.put("received".getBytes()); writeBuff.flip();//When you need to start reading after writing the data, you will postion Reset to 0 and limit Set current postion. while (true) { int nReady = selector.select(); Set<SelectionKey> keys = selector.selectedKeys(); Iterator<SelectionKey> it = keys.iterator(); while (it.hasNext()) { SelectionKey key = it.next(); it.remove(); if (key.isAcceptable()) { // Create a new connection and register the connection to selector Come on, and, // Declare this channel Only interested in read operations. SocketChannel socketChannel = ssc.accept(); socketChannel.configureBlocking(false); socketChannel.register(selector, SelectionKey.OP_READ); } else if (key.isReadable()) { SocketChannel socketChannel = (SocketChannel) key.channel(); readBuff.clear(); socketChannel.read(readBuff); readBuff.flip(); System.out.println("received : " + new String(readBuff.array())); key.interestOps(SelectionKey.OP_WRITE); } else if (key.isWritable()) { writeBuff.rewind(); SocketChannel socketChannel = (SocketChannel) key.channel(); socketChannel.write(writeBuff); key.interestOps(SelectionKey.OP_READ); } } } } catch (IOException e) { e.printStackTrace(); } } }
client
package com.tcc.test.nio.selector; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; /** * Copyright (C) @2021 * * @author: tcc * @version: 1.0 * @date: 2021/11/28 * @time: 18:47 * @description: */ public class WebClient { public static void main(String[] args) throws IOException { try { SocketChannel socketChannel = SocketChannel.open();//// Get one Socket passageway socketChannel.connect(new InetSocketAddress("127.0.0.1", 8000));//Which connection ip And ports ByteBuffer writeBuffer = ByteBuffer.allocate(32); ByteBuffer readBuffer = ByteBuffer.allocate(32); writeBuffer.put("hello".getBytes()); writeBuffer.flip(); while (true) { writeBuffer.rewind();//rewind(): Repeatable reading socketChannel.write(writeBuffer); readBuffer.clear(); socketChannel.read(readBuffer); } } catch (IOException e) { } } }