Quickly understand the content of BIO and NIO threads in Java. It's enough to read this article

Keywords: Java network server

In this article, I want to complete the basic notes of the network programming model supported by Java, so that I and you can understand the I/O mode of network programming supported by Java.

Introduction (small stove)

Speaking of network programming, let's first understand the I/O modes of network programming supported by Java: BIO, NIO, and AIO. We may not hear the three names just mentioned, but we will certainly hear the concept of I/O. When you learn about threads, you will use the methods in the java.io package to complete the small implementation of threads.

Such as InputStream and OutPutStream. These are the basic input and output streams under the IO package. However, another part is network IO, such as the most common Socket or ServerSocket, which is the method under the java.net package.

In fact, the above is our initial Java foundation, that is, the content of BIO, that is, BIO(Blocking I/O).

Therefore, with the study of basic knowledge, I want to enter the follow-up more in-depth content.

1,BIO

1.1 basic concept of Bio

Java BIO is the traditional JAVA IO programming. It has corresponding classes and interfaces under the java.io package.

Synchronization and blocking: the server implementation mode is one thread per connection, that is, when the client has a connection request, the server needs to start a thread for processing. If the connection does nothing, it will cause unnecessary thread overhead.

Additional: explain the concepts of synchronization and blocking first, so that you can understand the basic concepts of BIO first:

Synchronization: when a user triggers an IO operation, it will wait or check whether the IO operation is ready in other ways.

For example, if you go to play the king's glory and open a peak game, you will die waiting to enter the selection position of the peak game.

Blocking: it means that when preparing to read or write a file, if there is nothing to read or write at that time, the program will enter the waiting state. Until something can be read or written.

For example, you're going to choose the confrontation Road, but you're on the fifth floor. The front teammates don't choose heroes, so you have to wait for them to choose heroes before it's your turn.

BIO is a combination of synchronization + blocking. It looks very rigid and likes to wait for others. But this is bound to miss itself.

1.2 BIO architecture

The basic architecture of BIO is shown as follows:

This is the basic BIO. Let me introduce the basic process:

  1. Start a server and wait for the client to connect.
  2. Start a client to communicate with the server. Of course, the communication architecture is like this, that is, one client and one thread.
  3. The client sends a request, and the server responds according to the thread. Here, the server can wait or reject.
  4. If the response is successful, the client thread will wait for the request to end and continue execution.

1.3 summarize BIO's architecture

Based on the above process and diagram, we can think about its defects and usage scenarios:

  1. In fact, it seems that if the client is simply added, the thread allocation is given first-class. Then the server has to start more and more threads, which is very stressful.
  2. The client may not always be doing things after initiating a connection. At this time, the server also needs to be maintained, which will also cause unnecessary pressure.

Of course, the use scenario should be in an architecture with a small and fixed number of connections.

1.4 contents of BIO demonstration

1. First, add the following code in the server section:

public static void main(String[] args) {
        //1. Creation of thread pool
        ExecutorService executorService = Executors.newCachedThreadPool();
        //The server
        try {
            ServerSocket serverSocket=new ServerSocket(10086);
            System.out.println("Server startup--The port number is 10086");
            while (true){
                //Clients connected to the server
                Socket client = serverSocket.accept();
                System.out.println("Client connection succeeded!");
                //Create a new thread to communicate with each client connection
                executorService.execute(()->{
                    System.out.print("thread  id:"+Thread.currentThread().getId());
                    System.out.println("Thread name:"+Thread.currentThread().getName());
                    try {
                        int len=0;
                        byte[] byteArr=new byte[1024];
                        //Read data from client
                        InputStream inputStream = client.getInputStream();
                        while((len=inputStream.read(byteArr))!=-1){
                            String msg=new String(byteArr);
                            System.out.println("Message from client:"+msg);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

2. Test the talnet client

In the above step, if talnet displays (not external or internal files), go to control panel - > program - > start or close windows service - > click "Telnet client" and wait

Generally, you can only enter one character after entering talnet, and press ctrl +] to output the string.

2. NIO basic introduction

2.1 basic concept of NiO

We come to the content of NIO, which is synchronous and non blocking. The server implementation mode is that one thread processes multiple requests (connections), that is, the connection requests sent by the client will be registered on the multiplexer, and the multiplexer will process the I/O requests after polling.

Let's talk about the concept of non blocking based on the two concepts of bio (synchronous blocking):

Non blocking: if nothing is readable or writable, the read-write function returns immediately without waiting. It won't wait like a jam.

For example, if you want to enter the king matching, you don't have to wait for the advanced people in front to match. You can go to the mall to see what heroes can buy or go to activities to get gold coins. When the matching system is your turn, you can enter your matching selection stage.

First of all, NIO and BIO are implemented in different ways. Of course, their functions and purposes are the same. BIO processes data in stream mode, while NIO processes data in block mode, so NIO has high efficiency.

Secondly, according to the Internet, NIO introduced the NIO framework (java.nio package) in Java 1.4. java provides a series of improved input and output new features. These are collectively referred to as NIO. Others become New IO.

2.2 working principle

  1. First, a thread corresponds to a selector, and a thread corresponds to multiple connections (channels). Each channel corresponds to a Buffer.
  2. Secondly, multiple channels can be registered with a selector, and events determine which channel the selector switches to.
  3. Secondly, data is read and written through the Buffer. The flow in BIO is unidirectional, either input or output. The Buffer of NIO can be read and written in both directions, that is, it can be switched by flip method.
  4. Finally, the channel is bidirectional and can return to the underlying operating system.

3. NIO core Buffer buffer

3.1 Buffer concept

Buffer: a buffer is essentially a block of memory from which data can be written and then read. This memory is then packaged as a NIO Buffer object.

As shown above, the Channel provides a Channel to read data from a file or network, but the data read or written is through the Buffer.

3.2 Buffer class display

In NIO, Buffer is not only a parent class, but also an abstract class with many subclasses.

attributedescribe
capacityCapacity; That is, the maximum amount of data that can be accommodated; It is set when the buffer is created and cannot be changed.
positionPosition, the index of the next element to be read or written. This value will be changed every time the buffer data is read and written, so as to prepare for the next reading and writing.
limitIndicates the current end point of the Buffer. You cannot read or write to the position of the Buffer that exceeds the limit. In write mode, limit is equal to Buffer capacity. When switching Buffer to read mode, limit indicates the maximum data you can read. Therefore, when the Buffer is switched to the read mode, the limit will be set to the position value in the write mode.

Next is a screenshot showing the Buffer:

The methods shown here are capacity, position and limit.

3.3 basic usage of buffer

  1. First create the cache.
  2. Call the flip method to change the cache to read mode
  3. Read data from Buffer
  4. Use clear() or compact()
public static void main(String[] args) {
        // 1. Create a Buffer and write data to the Buffer
        ByteBuffer buffer= ByteBuffer.allocate(1024);//Creates an indirect buffer of the specified capacity
        //Write data
        buffer.put((byte) 'h');
        buffer.put((byte) 'i');
        buffer.put((byte) ',');
        buffer.put((byte) 'x');
        buffer.put((byte) 'i');
        buffer.put((byte) 'a');
        buffer.put((byte) 'o');
        buffer.put((byte) 'a');
        buffer.put((byte) 'i');
        buffer.put((byte) 'f');
        buffer.put((byte) 'u');
        // 2. Call the flip() method to change the buffer to read mode
        buffer.flip();
        // 3. Read data from Buffer
        byte[] data=new byte[buffer.limit()];
        buffer.get(data);
        System.out.println(new String(data));
        // 4. Call the clear() method or the compact() method
        buffer.clear();
        buffer.compact();
    }

The output is as follows:

4. NIO core Channel - Channel

4.1 Channel introduction

Channel: similar to stream in BIO, it establishes a connection to files or hardware devices.

Data can be read from the channel and written to the channel, but the reading and writing of the stream is usually one-way.

Channels can read and write asynchronously. The data in the channel must always be read to a Buffer or written from a Buffer.

4.2 implementation and usage of channel

Common Channel classes include FileChannel, datagram Channel, ServerSocketChannel and SocketChannel.

  • FileChannel reads and writes data from a file.
  • Datagram channel can read and write data in the network through UDP.
  • SocketChannel can read and write data in the network through TCP.
  • ServerSocketChannel can listen for new TCP connections, just like a Web server. A SocketChannel will be created for each new incoming connection.

I only use FileChannel here

public static void main(String[] args) throws IOException {
        String msg="hi,xiaoaifu";
        String fileName="channel01.txt";
        //Create an output stream
        FileOutputStream fileOutputStream=new FileOutputStream(fileName);
        //Get the same channel -- the actual type of channel is FileChannelImpl
        FileChannel channel=fileOutputStream.getChannel();
        //Create a buffer
        ByteBuffer buffer= ByteBuffer.allocate(1024);
        //Writes information to the buffer
        buffer.put(msg.getBytes());
        //Switch between read and write to buffer
        buffer.flip();
        //Writes the data in the buffer to the channel
        int num=channel.write(buffer);
        System.out.println("Write complete!"+num);
        fileOutputStream.close();
    }

Here, Buffer is a write operation, and you can see the complete content. When writing, the pointer to position moves to the beginning.

5. NIO Selector core Selector

5.1 Selector concept

Selector s are commonly referred to as selectors. 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.

If NIO in java is used, it can be processed in non blocking IO mode. In this mode, one thread can be used to process a large number of client connection requests.

Only when there is a real read-write event in the connection can read and write, which greatly reduces the system overhead, and there is no need to create a thread for each connection and maintain multiple threads.

5.2 implementation of NiO

1. Server encoding

public class NIOServer {
    public static void main(String[] args) throws IOException {
        //Create server
        ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
        //Bind server port
        serverSocketChannel.bind(new InetSocketAddress(10086));
        //Set to unblocked channel - important
        serverSocketChannel.configureBlocking(false);
        //Create selector object
        Selector selector = Selector.open();
        //Register the channel in the selector and listen for the connection time of the request
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        //Loop waiting for client connection
        while(true){
            if(selector.select(3000)==0){
                System.out.println("server: The waiting flowers are all gone.....I'll be busy first......");
                continue;
            }
            //Get SelectionKey collection
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while(iterator.hasNext()){
                SelectionKey key = iterator.next();
                if(key.isAcceptable()){
                    //Get connected clients
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    //Set to non blocking
                    socketChannel.configureBlocking(false);
                    System.out.println("There is a client connection!!! Generated SocketChannel: "+socketChannel.hashCode());
                            //register
                            socketChannel.register(selector, SelectionKey.OP_READ,
                                    ByteBuffer.allocate(1024));
                }
                if(key.isReadable()){
                    SocketChannel socketChannel= (SocketChannel) key.channel();
                    ByteBuffer buffer= (ByteBuffer) key.attachment();
                    socketChannel.read(buffer);
                    System.out.println("Message from client:"+new String
                            (buffer.array()));
                }
                iterator.remove();
            }
        }
    }
}

2. Client encoding

public static void main(String[] args) throws IOException {
        SocketChannel socketChannel=SocketChannel.open();
        socketChannel.configureBlocking(false);
        InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 10086);
        if(!socketChannel.connect(inetSocketAddress)){
            while(!socketChannel.finishConnect()){
                System.out.println("Client: Trying to connect.....You can do something else first");
            }
        }
        String msg="hi,xiaoaifu";
        ByteBuffer buffer=ByteBuffer.wrap(msg.getBytes());
        socketChannel.write(buffer);
        System.in.read();
    }

6. Summary

We have understood the basic contents of BIO and NIO. In summarizing their programming, we can first understand their underlying principles, namely:

  • BIO: Block IO synchronous blocking IO is the traditional IO we usually use. It is characterized by simple mode, convenient use and low concurrent processing capacity.
  • NIO: Non IO synchronous non blocking IO is an upgrade of traditional io. The client and server communicate through Channel to realize multiplexing.

Thank you for reading, thank you!

Posted by richblend on Fri, 05 Nov 2021 15:16:24 -0700