Knowledge:

  1. The concept of blocking, the difference between synchronous and asynchronous
  2. Bio and multiplexing
  3. NIO profile
  4. NIO Buffer
  5. NIO Channel
  6. NIO Selector
  7. NIO Reactor
  8. Nio-based chat room example

Learning NIO we know first pre concepts: 1) the blocking and non-blocking blocking and non-blocking is a process at the time of access to data, a way of dealing with the data is ready, when the data is not ready to block: often need to wait for the data in the buffer ready to handle other things later, or you’ve been waiting for. Non-blocking: when our process models our data buffer

Asynchronous: An application can process multiple I/O reads and writes at a time. An application waits for the notification from the OPERATING system. Synchronous: An application can process only one I/O read and write at a time

Let’s move on to the picture below

We officially started learning NIO

JAVA NIO concept

Java NIO is a Java 1.4 interface, and NIO is a non-blocking interface. Some people think that NIO is new, but it is also true. Comparison between BIO (Block IO) and Nio (Non-block IO)

Nio is more efficient than IO because it mainly uses blocks. There are two sets of nios in the JavaAPI: 1) nio for standard input and output and 2) network programming nio Io processes data as streams and nio processes data as blocks. Stream-oriented IO processes one byte at a time, producing one byte for an input stream and consuming one byte for an output stream. In block-oriented IO, each operation produces or consumes a chunk of data in a single step. The way it reads data and the way it writes data does not have to be implemented through the channel to manipulate the buffer. Core components include Channels Buffers Selectors

Java NIO Buffer

1) Buffer introduction: Buffer, nature is an array, but it is a special array, a buffer object built in some mechanism, to be able to track and record the state of the buffer, if we use the get method to get data from the buffer or use the put method data written to the buffer, can cause the change of state of the buffer In the buffer, The most important properties are the following three, which work together to track changes in the state of the contents of the buffer Specifies the index of the next element to be written or read. Its value is automatically updated by the get()/put() methods. Position is initialized to 0 when a new Buffer object is created. Buffer operation and the operational range of operational space, specify how much data will need to be removed, or how much space can fit into a data 3) capacity: specifies the biggest data can be stored in the buffer capacity, in fact, it specifies the size of the underlying array, or at least specifies the allowed us to use the capacity of the underlying array.

If we create a new Bytebuffer object with a capacity of 10, at initialization time, the values of the three attributes have some relative size relationships: 0<=position<=limit<= Capacity Position is set to 0, and limit and Capacity are set to 10. Capacity will not change during future use of Bytebuffer, and the other two values will change as shown in the figure below:

Now we can read some data from the channel into the buffer, notice that reading data from the channel is like writing data into the buffer. If four of its own data are read, position is 4, the next byte index to be written is 4, and limit is still 10





package com.Allen.buffer;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class testBufferDemo01 {
	public static void main(String[] args) throws IOException {
		String fileURL="F://a.txt"; FileInputStream fis=new FileInputStream(fileURL); FileChannel channel=fis.getChannel(); ByteBuffer =ByteBuffer. Allocate (10); output("init", buffer); / / read channel. Read (buffer); output("read", buffer);
		buffer.flip();
		output("flip", buffer);		
		while (buffer.hasRemaining()) {
			byte b=buffer.get();
		}
		output("get", buffer);
		buffer.clear();
		output("clear", buffer);
		fis.close();
	}
	
	public static void output(String string,ByteBuffer buffer){
		System.out.println(string);
		System.out.println(buffer.capacity()+":"+buffer.position()+":"+buffer.limit()); }}Copy the code

The results of

Java NIO channels

A channel is an object through which data can be read and written, all of which is handled by buffer objects. We never write bytes directly to the channel, instead writing data to a buffer containing one or more bytes. Instead of reading a byte directly, data is read from a channel into a buffer and retrieved from the buffer. Nio provides a variety of channel objects, all of which implement the Channel interface.

Whenever data is read, it is read not directly from the channel, but from the channel to the buffer, So reading data using NIO can be divided into three steps: 1) getting a Channel from a FileInputStream 2) creating a Buffer 3) reading data from a Channel into a Buffer. Here is an example of NIO reading a copy file

package com.allen.test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class testNio {
	public static void main(String[] args) throws IOException {
		String oldFileUrl="E://1.txt";
		String newFileUrl="E://2.txt";
		FileInputStream fis=new FileInputStream(oldFileUrl);
		FileChannel inChannel=fis.getChannel();
		ByteBuffer bf=ByteBuffer.allocate(1024);
		FileOutputStream fos=new FileOutputStream(newFileUrl);
		FileChannel outChannel=fos.getChannel();
		while(true){
			int eof=inChannel.read(bf);
			if(eof==-1){
				break;
			}else{ bf.flip(); outChannel.write(bf); bf.clear(); }}inChannel.close(); fis.close(); outChannel.close(); fos.close(); }}Copy the code

JAVA NIO Selector

A Selector is usually called a Selector, but you can also translate it as a multiplexer. 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.

With selector, you can handle all channels in one thread. Switching between threads is expensive for an operating system, and each thread consumes a certain amount of system resources, so for a system, fewer threads is better (but not always, if the CPU has multiple cores, not multitasking is a waste of CPU capacity).

Selector selector=Selector.open(); Registered channel to channel on the selector. The configureBlocking (false) SelectionKey key = channel. The register (the selector, SelectionKey OP_READ)

Channels registered to the server must be set to asynchronous mode, otherwise asynchronous IO won’t work, which means we can’t register a Filechannel with a selector, because Filechannel doesn’t have an asynchronous mode, but socketchannels do

The second argument to the Register method, which is an interst set, means that the registered selector is interested in those transactions in the channel. There are four types of events: Read Write Connect Accept, where a channel triggers a time when the event is read, and all channels successfully connect to another server are called Connect Ready. A serverSocketChanel ready to accept a new connection is called Connect Ready. A data readable channel can be said to be read ready. The channel waiting for data to be written is ready. OP_WRITE Read: SelectionKey.OP_READ Accept: SelectionKey.OP_ACCEPT Connect: SelectionKey. Selectionkey. OP_CONNECT If multiple events are involved, Can be written as (or) Int = SelectionKey. OP_READ | SelectionKey. OP_ACCEPT SelectionKey said channel selector on the registration, The selector key is used to get the selector and the registered channel.selector. Once one or more channels have been registered with selector, you can call the overloaded select method to return channels that are ready for the event you are interested in.

JAVA NIO Reactor

Blocking /IO communication model

















Six instances

The server

package com.allen.nio; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.Channel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.charset.Charset; import java.util.HashSet; import java.util.Iterator; import java.util.Set; /** * Network multi-client chat room * Function 1: The client is connected to the server through Java NIO, support multi-client connection * function 2: When the client connects for the first time, the server prompts you to enter a nickname. If the nickname has been used, it prompts you to re-enter it. If the nickname is unique, the login is successful. After the client logs in, it sends the configured welcome information and online number to the client and notifies other clients that the client is online. * Function 4: The server receives the input information from the logged in client and forwards the information to other logged clients. */ public class NIOServer {private int port = 8080; private Charset charset = Charset.forName("UTF-8"); Private static HashSet<String> users = new HashSet<String>(); private static String USER_EXIST ="System prompts: this nickname already exists, please change a nickname."; Private static String USER_CONTENT_SPILIT = Private static String USER_CONTENT_SPILIT ="# @ #"; private Selector selector = null; public NIOServer(int port) throws IOException{ this.port = port; ServerSocketChannel server = ServerSocketChannel.open(); Server.bind (new InetSocketAddress(this.port)); // Set the highway level server.bind(new InetSocketAddress(this.port)); server.configureBlocking(false); Selector = selector. Open (); Server. register(selector, selectionkey.op_accept); System.out.println("Service started, listening port is:"+ this.port); } public void listener() throws IOException{// The CPU working frequency is controllablewhile(true) {// In the polling, we service hall, exactly how many people are queuing intwait = selector.select();
            if(wait= = 0)continue; Set<SelectionKey> keys = selectedKeys(); Iterator<SelectionKey> Iterator = keys.iterator();while(iterator.hasNext()) { SelectionKey key = (SelectionKey) iterator.next(); Iterator.remove (); iterator.remove(); iterator.remove(); iterator.remove(); iterator.remove(); // process(key); }}} public void process(SelectionKey key) throws IOException {// Check whether the client has entered the service lobby and is ready for interactionif(key.isAcceptable()){ ServerSocketChannel server = (ServerSocketChannel)key.channel(); SocketChannel client = server.accept(); // Client. ConfigureBlocking (false); // Register the selector, set it to read mode, receive a connection request, create a SocketChannel, register it with the selector, and then the data for that connection, This SocketChannel handles client.register(selector, selectionkey.op_read); InterestOps (selectionkey.op_accept); // Set the corresponding channel to be ready to accept requests from other clients. // System.out.println("There is client connection with IP address :" + sc.getRemoteAddress());
            client.write(charset.encode("Please enter your nickname")); } // Handle data read requests from clientsifSocketChannel client = (SocketChannel)key.channel(); ByteBuffer buff = ByteBuffer. Allocate (1024); StringBuilder content = new StringBuilder(); try{while(client.read(buff) > 0)
                {
                    buff.flip();
                    content.append(charset.decode(buff));
                    
                }
//                System.out.println("Secondary IP address is:" + sc.getRemoteAddress() + "Get the message:"+ content); InterestOps (selectionkey.op_read); }catch (IOException io){ key.cancel();if(key.channel() != null)
                {
                	key.channel().close();
                }
            }
            if(content.length() > 0) { String[] arrayContent = content.toString().split(USER_CONTENT_SPILIT); // Register a userif(arrayContent ! = null && arrayContent.length == 1) { String nickName = arrayContent[0];if(users.contains(nickName)) {
                    	client.write(charset.encode(USER_EXIST));
                    } else {
                        users.add(nickName);
                        int onlineCount = onlineCount();
                        String message = "Welcome" + nickName + "Enter the chat room! Number of current online users :"+ onlineCount; broadCast(null, message); }} // After registration, send messageelse if(arrayContent ! = null && arrayContent.length > 1) { String nickName = arrayContent[0]; String message = content.substring(nickName.length() + USER_CONTENT_SPILIT.length()); message = nickName +"说 : " + message;
                    if(users.contains(nickName)) {// broadCast(client, message) is not sent back to the client that sends this content; }}}}} //TODO: TODO: TODO: TODO: TODO: TODO: TODO: TODO: TODOonlineCount() {
        int res = 0;
        for(SelectionKey key : selector.keys()){
            Channel target = key.channel();
            
            if(target instanceof SocketChannel){ res++; }}returnres; } public void broadCast(SocketChannel Client, String Content) throws IOException {// broadCast data to all SocketChannelsfor(SelectionKey key : selector.keys()) { Channel targetchannel = key.channel(); // If the client is not empty, the message is not sent back to the sending clientif(targetchannel instanceof SocketChannel && targetchannel != client) {
                SocketChannel target = (SocketChannel)targetchannel;
                target.write(charset.encode(content));
            }
        }
    }
    
    
    public static void main(String[] args) throws IOException {
        new NIOServer(8080).listener();
    }
}
Copy the code

The client

package com.allen.nio;

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.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;

public class NIOClient {

	private final InetSocketAddress serverAdrress = new InetSocketAddress("localhost", 8080);
    private Selector selector = null;
    private SocketChannel client = null;
    
    private String nickName = "";
    private Charset charset = Charset.forName("UTF-8");
    private static String USER_EXIST = "System prompts: this nickname already exists, please change a nickname.";
    private static String USER_CONTENT_SPILIT = "# @ #"; Public NIOClient() throws IOException{// Connect to remote host IP and port client = socketchannel. open(serverAdrress); client.configureBlocking(false); // selector = selector. Open (); client.register(selector, SelectionKey.OP_READ); } public voidsession(){// start a thread to read data from the server new Reader().start(); // start a thread to write data to the server. New Writer().start(); } private class Writer extends Thread{ @Override public voidrunScanner scan = new Scanner(system.in); Scanner scan = new Scanner(system.in);while(scan.hasNextLine()){
		            String line = scan.nextLine();
		            if("".equals(line)) continue; // Empty messages are not allowedif("".equals(nickName)) {
		            	nickName = line;
		                line = nickName + USER_CONTENT_SPILIT;
		            } else{ line = nickName + USER_CONTENT_SPILIT + line; } // client.register(selector, SelectionKey.OP_WRITE); client.write(charset.encode(line)); } scan. Close (); }catch(Exception e){ } } } private class Reader extends Thread { public voidrun() {try {// pollingwhile(true) {
                    int readyChannels = selector.select();
                    if(readyChannels == 0) continue; Set<SelectionKey> selectedKeys = selector.selectedKeys(); Iterator<SelectionKey> keyIterator = selectedkeys.iterator ();while(keyIterator.hasNext()) {
                         SelectionKey key = (SelectionKey) keyIterator.next();
                         keyIterator.remove();
                         process(key);
                    }
                }
            }
            catch (IOException io){
            	
            }
        }

        private void process(SelectionKey key) throws IOException {
            if(key.isreadable ()){// Use NIO to read data from a Channel. This is the same as the global client variable, because only one SocketChannel is registered. Sc = (SocketChannel)key.channel(); ByteBuffer buff = ByteBuffer.allocate(1024); String content ="";
                while(sc.read(buff) > 0) { buff.flip(); content += charset.decode(buff); } // If the notification name already exists, you need to change the nicknameif(USER_EXIST.equals(content)) {
                	nickName = ""; } System.out.println(content); key.interestOps(SelectionKey.OP_READ); } } } public static void main(String[] args) throws IOException { new NIOClient().session(); }}Copy the code