Junior sister learning java IO: using Selector to send good person card

brief introduction

NIO has three treasures: Buffer,Channel and Selector. This article will introduce the last of NIO's three piece set of selectors, and help junior sister send a good person card on the basis of understanding the selectors. Let's start.

Introduction to Selector

Younger martial sister: elder martial brother F, my peach blossom is a little prosperous recently. Several elder martial brothers say hello to me inexplicably, but I'm focused on my work and don't want to talk about these things. After all, a career makes a family. I can't refuse it directly. Is there any more obscure way for them to give up this idea?

More atwww.flydean.com

I pondered this question for about 0.001 seconds, so I gave the answer: send them a good person card, and I won't pester you again.

Younger martial sister: elder martial brother F, what's the use of sending them good person cards?

Then we can only cut off the contact with them, and make a one size fits all break. ha-ha.

Well, little sister, aren't you learning NIO recently? Just in time, we can use the Selector to simulate the process of sending a good person card.

If your senior brothers Zhiwei and Zidan want to establish contact with you and everyone wants to establish a communication channel with you, then you need to create two channels.

The two channels are actually good. If more than one person wants to establish contact channels with you at the same time, to maintain these channels, you need to maintain the connection, which wastes resources.

But these connections are not always transmitted with messages, so in fact, most of the time these channels are wasted.

If the Selector is used, only one thread can be enabled to listen for message changes of the channel. This is the Selector.

As can be seen from the above figure, the Selector listens to three different channel s and then hands them over to a processor for processing, thus saving resources.

Create Selector

First look at the definition of selector:

public abstract class Selector implements Closeable

Selector is an abstract class and implements Closeable, indicating that the selector can be closed.

Although the Selector is an abstract class, you can simply create it through open:

Selector selector = Selector.open();

If you look at the implementation of open, you can find a very interesting phenomenon:

public static Selector open() throws IOException {
        return SelectorProvider.provider().openSelector();
    }

The open method calls the openSelector method in the SelectorProvider.

Look at the implementation of the provider:

 public SelectorProvider run() {
   if (loadProviderFromProperty())
        return provider;
    if (loadProviderAsService())
        return provider;
      provider = sun.nio.ch.DefaultSelectorProvider.create();
      return provider;
    }
 });

There are three cases where a selectorprovider can be loaded, if the system property specifies java.nio.channels.spi.SelectorProvider , then load from the specified property.

If no properties are specified directly, load from ServiceLoader.

Finally, if none is found, use the default DefaultSelectorProvider.

As for the usage of ServiceLoader, we will have a special article later. There is no more explanation here.

Register Selector to Channel

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress("localhost", 9527));
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

If it is on the Server side, we need to create a ServerSocketChannel, bind the Server address and port, and then set Blocking to false. Because we use the Selector, it is actually a non Blocking IO.

Note that FileChannels cannot use Selector because it is a blocking IO.

Junior sister: brother F, why is FileChannel blocked? Isn't it faster to be non blocking?

Younger martial sister, what is the purpose of using FileChannel? For the purpose of reading the file, it must be read all the time. It is impossible to read this channel for a while and then read another channel. For each channel, before the file is read, it is busy. There is no need to switch in the channel.

Finally, we register the created Selector into the channel.

SelectionKey

SelectionKey represents the event we want to listen to.

In general, there are four types of events:

  • SelectionKey.OP_READ indicates that the server is ready to read data from the channel.
  • SelectionKey.OP_WRITE indicates that the server is ready to write data to the channel.
  • SelectionKey.OP_CONNECT indicates that the client attempts to connect to the server
  • SelectionKey.OP_ACCEPT means that the server accepts a client's request
public static final int OP_READ = 1 << 0;
public static final int OP_WRITE = 1 << 2;
public static final int OP_CONNECT = 1 << 3;
public static final int OP_ACCEPT = 1 << 4;

We can see that the above four events are defined by bit operations. If the four events are used or combined, we will get the interestOps in the SelectionKey.

Similar to interestOps, SelectionKey also has a readyOps.

One represents the operation of interest, one represents the operation of ready.

Finally, the SelectionKey can attach an Object when registering. For example, we can save the channel id in this Object:

SelectionKey key = channel.register(
  selector, SelectionKey.OP_ACCEPT, object);
key.attach(Object);
Object object = key.attachment();

The object can be passed in at register time or the attach method can be called.

Finally, we can get the object through the attachment method of key.

selector and SelectionKey

We passed selector.select() a blocking operation to obtain a ready channel.

Then we call selector.selectedKeys() to get the SelectionKey object.

In the SelectionKey object, we process the corresponding message by judging the ready event.

General example

Next, let's connect the previous ones, and first build a chat server for the younger martial sister:

public class ChatServer {

    private static String BYE_BYE="bye";

    public static void main(String[] args) throws IOException, InterruptedException {
        Selector selector = Selector.open();
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress("localhost", 9527));
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        ByteBuffer byteBuffer = ByteBuffer.allocate(512);

        while (true) {
            selector.select();
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> iter = selectedKeys.iterator();
            while (iter.hasNext()) {
                SelectionKey selectionKey = iter.next();
                if (selectionKey.isAcceptable()) {
                    register(selector, serverSocketChannel);
                }
                if (selectionKey.isReadable()) {
                    serverResonse(byteBuffer, selectionKey);
                }
                iter.remove();
            }
            Thread.sleep(1000);
        }
    }

    private static void serverResonse(ByteBuffer byteBuffer, SelectionKey selectionKey)
            throws IOException {
        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
        socketChannel.read(byteBuffer);
        byteBuffer.flip();
        byte[] bytes= new byte[byteBuffer.limit()];
        byteBuffer.get(bytes);
        log.info(new String(bytes).trim());
        if(new String(bytes).trim().equals(BYE_BYE)){
            log.info("Better to say goodbye than not!");
            socketChannel.write(ByteBuffer.wrap("bye".getBytes()));
            socketChannel.close();
        }else {
            socketChannel.write(ByteBuffer.wrap("You're a good person".getBytes()));
        }
        byteBuffer.clear();
    }

    private static void register(Selector selector, ServerSocketChannel serverSocketChannel)
            throws IOException {
        SocketChannel socketChannel = serverSocketChannel.accept();
        socketChannel.configureBlocking(false);
        socketChannel.register(selector, SelectionKey.OP_READ);
    }
}

In the above example, we need to pay attention to two points. In the loop traversal, when selectionKey.isAcceptable When the server receives a new client connection, we need to call the register method and register an OP_READ event to this new SocketChannel, and then continue to traverse.

Second, we define a stop word. When we receive the stop word, we will directly close the client channel.

Take a look at the client code:

public class ChatClient {

    private static SocketChannel socketChannel;
    private static ByteBuffer byteBuffer;

    public static void main(String[] args) throws IOException {

        ChatClient chatClient = new ChatClient();
        String response = chatClient.sendMessage("hello Junior sister!");
        log.info("response is {}", response);
        response = chatClient.sendMessage("can I?");
        log.info("response is {}", response);
        chatClient.stop();

    }

    public void stop() throws IOException {
        socketChannel.close();
        byteBuffer = null;
    }

    public ChatClient() throws IOException {
        socketChannel = SocketChannel.open(new InetSocketAddress("localhost", 9527));
        byteBuffer = ByteBuffer.allocate(512);
    }

    public String sendMessage(String msg) throws IOException {
        byteBuffer = ByteBuffer.wrap(msg.getBytes());
        String response = null;
        socketChannel.write(byteBuffer);
        byteBuffer.clear();
        socketChannel.read(byteBuffer);
        byteBuffer.flip();
        byte[] bytes= new byte[byteBuffer.limit()];
        byteBuffer.get(bytes);
        response =new String(bytes).trim();
        byteBuffer.clear();
        return response;

    }
}

There is nothing special about the client code. You need to pay attention to the Buffer reading.

Final output:

server received: Info com.flydean.ChatServer  -Hello little sister!
client received: Info com.flydean.ChatClient  -Response is you are a good man
 server received: INFO com.flydean.ChatServer  -Can we?
client received: Info com.flydean.ChatClient  -Response is goodbye

Explain the whole process: Zhiwei has established a connection with junior sister. Zhiwei says hello to junior sister. Junior sister sends Zhiwei a good person card. Zhiwei doesn't give up. She wants to keep pestering. Younger martial sister replies goodbye and closes the channel.

summary

This paper introduces the role of Selector and channel in the process of sending good person card.

Author of this article: the flydean program

Link to this article: http://www.flydean.com/java-io-nio-selector/

Source: flydean's blog

Welcome to my official account: the procedures, the more wonderful things waiting for you!

Tags: Java Spring Blockchain

Posted on Sat, 13 Jun 2020 21:15:52 -0400 by waradmin