The previous two articles introduced buffers and channels in NIO. With that in mind, this article introduces another important concept —-Selector. We know that switching system threads consumes system resources, and if we were to manage each connection with a single thread, that resource would be too expensive, so we could use Selector. A thread can manage multiple channels with Selector, as shown in the following figure:
The Selector using
Open the
You get a Selector object before you use it
Selector selector = Selector.open();
Copy the code
registered
To register a Channel with a Selector, it must be non-blocking. So a FileChannel can’t register a Selector. If the registration is not call configureBlocking method will be thrown IllegalBlockingModeException anomalies.
SelectionKey
There are four kinds of selectionkeys
- OP_ACCEPT
- OP_CONNECT
- OP_WRITE
- OP_READ
ServerSocketChannel registered
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
Copy the code
The Operation Set of ServerSocketChannel must be OP_ACCEPT. If OP_CONNECT, OP_WRITE, or OP_READ is added during registration, an exception is reported. For example, write as follows
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT | SelectionKey.OP_CONNECT);
Copy the code
The following exception is thrown
Exception in thread "main" java.lang.IllegalArgumentException
at java.nio.channels.spi.AbstractSelectableChannel.register(AbstractSelectableChannel.java:199)
at java.nio.channels.SelectableChannel.register(SelectableChannel.java:280)
at com.nio.sample.selector.SelectorServerSocketChannelSample.main(SelectorServerSocketChannelSample.java:27)
Copy the code
ServerSocketChannel validOps can see that only OP_ACCEPT is valid
public final int validOps() {
return SelectionKey.OP_ACCEPT;
}
Copy the code
SocketChannel registered
socketChannel.register(selector, SelectionKey.OP_CONNECT);
Copy the code
The Operation Set of a SocketChannel can only be OP_CONNECT, OP_WRITE, or OP_READ. If OP_ACCEPT is added during SocketChannel registration, an exception is also reported.
SocketChannel validOps can see that only OP_READ, OP_WRITE, and OP_CONNECT are valid
public final int validOps() {
return (SelectionKey.OP_READ
| SelectionKey.OP_WRITE
| SelectionKey.OP_CONNECT);
}
Copy the code
After successful registration, we implement a demo, where the client and server interact:
Server:
public static void main(String[] args) throws Exception {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(9000));
serverSocketChannel.configureBlocking(false); Selector selector = Selector.open(); // configureBlocking Register when abnormal transactions / / Java nio. Channels. IllegalBlockingModeException serverSocketChannel. Register (selector, SelectionKey.OP_ACCEPT);while (true) {
int selected = selector.select();
if (selected > 0) {
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
iterator.remove();
if (selectionKey.isAcceptable()) {
System.err.println("Acceptable");
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) {
System.err.println("Readable");
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
ByteBuffer buffer = ByteBuffer.allocate(128);
socketChannel.read(buffer);
System.out.println("Receive data from client:" + new String(buffer.array()));
selectionKey.interestOps(SelectionKey.OP_WRITE);
} else if (selectionKey.isWritable()) {
System.err.println("Writable");
SocketChannel channel = (SocketChannel) selectionKey.channel();
String content = "Send data to client:" + System.currentTimeMillis();
ByteBuffer buffer = ByteBuffer.wrap(content.getBytes());
channel.write(buffer);
selectionKey.interestOps(SelectionKey.OP_READ);
}
}
}
}
}
Copy the code
Let’s look at the logic on the server side
1. The server registers the selector, then sets the interest set (OPS) to selectionKey. OP_ACCEPT and waits for the client to connect.
2, client connection to arrive, call to selectionKey. IsAcceptable () method, receiving client connection, and then get a channel, and place
Interest set to selectionKey. OP_READ Waits for data to be read from the channel.
InterestOps = selectionKey.op_write selectionKey.interestOps = selectionkey.op_write selectionKey.interestOps = selectionkey.op_write
InterestOps selectionKey.isWritable() is triggered to send data to the client, and selectionKey.interestOps is set again to
Selectionkey. OP_READ Waits for client data to arrive.
Client:
public static void main(String[] args) throws IOException {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
Selector selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_CONNECT);
socketChannel.connect(new InetSocketAddress("127.0.0.1", 9000));
while (true) {
int select = selector.select();
if (select > 0) {
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
if (selectionKey.isConnectable()) {
System.err.println("Connectable");
SocketChannel clientChannel = (SocketChannel) selectionKey.channel();
clientChannel.finishConnect();
selectionKey.interestOps(SelectionKey.OP_WRITE);
} else if (selectionKey.isReadable()) {
System.out.println("Readable");
SocketChannel channel = (SocketChannel) selectionKey.channel();
ByteBuffer buffer = ByteBuffer.allocate(128);
channel.read(buffer);
selectionKey.interestOps(SelectionKey.OP_WRITE);
System.out.println("Received server data" + new String(buffer.array()));
} else if (selectionKey.isWritable()) {
SocketChannel clientChannel = (SocketChannel) selectionKey.channel();
String str = "qiwoo mobile";
ByteBuffer buffer = ByteBuffer.wrap(str.getBytes());
clientChannel.write(buffer);
selectionKey.interestOps(SelectionKey.OP_READ);
System.out.println("Send data to the server"+ new String(buffer.array())); } iterator.remove(); }}}}Copy the code
Take a look at the logic on the server side
1. Send a connection request to the server.
2, selectionKey. IsConnectable () is triggered, the connection is successful, selectionKey. InterestOps set to selectionKey. OP_WRITE, ready to send data to the server.
InterestOps is set to selectionKey. OP_READ and waits for the data to come from the server.
InterestOps: selectionKey.op_write SelectionKey.op_write SelectionKey.op_write selectionKey.op_write selectionKey.op_write