Java IO learning

1 What is IO

We often say IO is the input and output of files or characters.

What is the difference between BIO,NIO and AIO
2.1 BIO
What is the BIO? Synchronous blocking in I/O mode, data reads and writes must be blocked in a thread waiting for them to complete. In the case that the number of active connections is not particularly high (less than 1000 for a single machine), this model is relatively good, allowing each connection to focus on its OWN I/O and simple programming model, without too much consideration of system overload, current limiting and other problems. The thread pool itself is a natural funnel to buffer connections or requests that the system can't handle. However, when faced with hundreds of thousands or even millions of connections, the traditional BIO model is powerless.Copy the code

A Java BIO example is shown in the following server-side code

public class BioServerDemo {

    public static void main(String[] args) {
        int port = 9999;
        try {
            // Create the Bio socket service
            ServerSocket serverSocket = new ServerSocket();
            // Bind the service port number
            serverSocket.bind(new InetSocketAddress(port));
            while (true) {// Block to get the client socket
               Socket socket = serverSocket.accept();
               // Enter the custom handle method to handle the client socket
               newThread(()-> handle(socket)).start(); }}catch(IOException e) { e.printStackTrace(); }}// Customize the Handle method to handle server and client sockets
    private static void handle(Socket socket) {
        byte[] bytes = new byte[1024];
        try {
            // Get the input stream and read out the data sent by the client
            int len = socket.getInputStream().read(bytes);
            System.out.println(new String(bytes,0,len));
            // Get the output stream and write back the data to the client
            socket.getOutputStream().write("this is server".getBytes());
            socket.getOutputStream().flush();
        } catch(IOException e) { e.printStackTrace(); }}}Copy the code

Client code presentation

public class BioClientDemo {

    public static void main(String[] args) {
        String host = "127.0.0.1";
        int port = 9999;
        try {
            Socket socket = new Socket(host,port);
            // Get the output stream and write data to the server
            socket.getOutputStream().write("hello,this is client".getBytes());
            socket.getOutputStream().flush();
            // Get the input stream and read the data from the server to the client
            byte[] bytes = new byte[1024];
            int len = socket.getInputStream().read(bytes);
            System.out.println(new String(bytes,0,len));
        } catch(IOException e) { e.printStackTrace(); }}}Copy the code
Advantages: simple model and simple coding Disadvantages: low performance summary: 1 BIO is used when a socket is connected to a client. When a socket is read or written, it will block until the thread finishes processing the socket. 2 Each connection creates a thread. The consumption of CPU to switch thread context also increases, and when the threshold is higher than a certain value, the performance will decrease by 3 instead of increasing the number of threads. Because one connection creates one thread, the model is relatively simple, which is suitable for scenarios with few client connections and can be handled simplyCopy the code
2.2 NIO
What is the NIO? NIO, called non-Block IO, supports buffer-oriented, channel-based APPROACHES to I/O operations. NIO provides two different Socket channel implementations, SocketChannel and ServerSocketChannel, that correspond to Socket and ServerSocket in the traditional BIO model. Both channels support both blocking and non-blocking modes. Blocking mode, like traditional support, is relatively simple, but has poor performance and reliability. Non-blocking mode is the opposite. Contains a selector used to process multiple channels using a single thread. It requires fewer threads to process these channels. Switching between threads can be expensive for an operating system. Selectors are useful in order to improve system efficiency. For low-load, low-concurrency applications, synchronous blocking I/O can be used for faster development and better maintenance; For high-load, high-concurrency (network) applications, use NIO's non-blocking mode to develop a thread model example that has a thread acting as a selector for all clients and socketchannels in the form of event listenersCopy the code

Java NIO single-threaded implementation of server-side code

public class NioServerDemo {
    public static void main(String[] args) throws IOException {
        int port = 9999;
        // Create niO Server
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(port));
        serverSocketChannel.configureBlocking(false);
        System.out.println("server start");
        / / get the selector
        Selector selector = Selector.open();
        // Register selector and accept events with the server
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        while (true){
            selector.select();
            // Get all the events managed by selector
            Set<SelectionKey> selectors = selector.selectedKeys();
            / / iteration selector
            Iterator<SelectionKey> selectionKeyIterable = selectors.iterator();
            while (selectionKeyIterable.hasNext()){
                SelectionKey key = selectionKeyIterable.next();
                // Process each selectionKey
                handle(key);
                // Remove it after processingselectionKeyIterable.remove(); }}}private static void handle(SelectionKey key){
        // Handle the accept event
        if (key.isAcceptable()){
            // Get the ServerSocket Channel, which handles socket connections to clients
            ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
            try {
                SocketChannel socketChannel = serverSocketChannel.accept();
                serverSocketChannel.configureBlocking(false);
                // Register read events
                serverSocketChannel.register(key.selector(),SelectionKey.OP_READ);
            } catch(IOException e) { e.printStackTrace(); }}else if (key.isReadable()){
            // Handle read events
            SocketChannel socketChannel = (SocketChannel) key.channel();
            ByteBuffer byteBuffer = ByteBuffer.allocate(512);
            byteBuffer.clear();
            int len = 0;
            try {
                len = socketChannel.read(byteBuffer);
            } catch (IOException e) {
                e.printStackTrace();
            }
            if(len ! = -1){
                System.out.println(new String(byteBuffer.array(),0,len));
            }
            // Write data to the client
            ByteBuffer bufferWrite = ByteBuffer.wrap("hello,this is server".getBytes());
            try {
                socketChannel.write(bufferWrite);
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    socketChannel.close();
                } catch(IOException e) { e.printStackTrace(); }}}}Copy the code

Multithreaded implementation

public class NioThreadPoolServerDemo {

    private ExecutorService executorService = Executors.newFixedThreadPool(20);
    private Selector selector;
    public static void main(String[] args) throws IOException {
        int port = 9999;
        NioThreadPoolServerDemo nioThreadPoolServerDemo = new NioThreadPoolServerDemo();
        nioThreadPoolServerDemo.initServer(port);
        nioThreadPoolServerDemo.listen();
    }
    
    private void initServer(int port) throws IOException {
        // Create bio service
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(port));
        serverSocketChannel.configureBlocking(false);
        // Enable the selector model
        this.selector = Selector.open();
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    }

    private void listen(a) throws IOException {
        while (true){
            selector.select();
            Set<SelectionKey> selectionKeySet = selector.selectedKeys();
            Iterator<SelectionKey> selectionKeyIterator = selectionKeySet.iterator();
            if (selectionKeyIterator.hasNext()){
                SelectionKey key = selectionKeyIterator.next();
                selectionKeyIterator.remove();
                // Handle the accept event
                if (key.isAcceptable()){
                    ServerSocketChannel server = (ServerSocketChannel) key.channel();
                    SocketChannel sc = server.accept();
                    sc.configureBlocking(false);
                    // Register the read event
                    sc.register(this.selector,SelectionKey.OP_READ);
                }else if (key.isReadable()){
                    key.interestOps(SelectionKey.OP_READ);
                    // When data is read or written, it is handed to the thread pool.
                    executorService.execute(new ThreadHandlerChannel(key));
                }
            }
            
            
        }
    }
}
class ThreadHandlerChannel extends Thread{
    private SelectionKey key;
    ThreadHandlerChannel(SelectionKey key){
        this.key = key;
    }

    @Override
    public void run(a) {
        // Handle read events
        SocketChannel socketChannel = (SocketChannel) key.channel();
        ByteBuffer byteBuffer = ByteBuffer.allocate(512);
        byteBuffer.clear();
        int len = 0;
        try {
            len = socketChannel.read(byteBuffer);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if(len ! = -1){
            System.out.println(new String(byteBuffer.array(),0,len));
        }
        // Write data to the client
        ByteBuffer bufferWrite = ByteBuffer.wrap("hello,this is server".getBytes());
        try {
            socketChannel.write(bufferWrite);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                socketChannel.close();
            } catch(IOException e) { e.printStackTrace(); }}}}Copy the code
Note 1 Event listening via selector 2 You need to actively register socket events with selectorCopy the code
2.3 AIO (asynchronius)
AIO is NIO 2. NIO 2, an improved version of NIO introduced in Java 7, is an asynchronous, non-blocking IO model. Asynchronous IO is implemented based on events and callbacks, meaning that an action is applied and returned, not blocked, and when the background processing is complete, the operating system notifies the appropriate thread to proceed with the subsequent action. AIO is short for asynchronous IO. Although NIO provides a non-blocking method for network operations, NIO's I/O behavior is synchronous. For NIO, our business thread is notified when the IO operation is ready, and then the thread performs the IO operation itself, which is synchronous.Copy the code
public class AioServerDemo {

    public static void main(String[] args) throws IOException {
        int port = 9999;
        AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open()
                .bind(new InetSocketAddress(port));
        // When a client connects,accept no longer blocks the thread and is handed over to the CompletionHandler for processing
        serverSocketChannel.accept(null.new CompletionHandler<AsynchronousSocketChannel, Object>() {

            @Override
            public void completed(AsynchronousSocketChannel client, Object attachment) {
                serverSocketChannel.accept(null.this);
                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                // Reading data no longer blocks execution and is handed over to the CompletionHandler after reading the data
                client.read(byteBuffer, byteBuffer, new CompletionHandler<Integer, ByteBuffer>() {
                    @Override
                    public void completed(Integer result, ByteBuffer attachment) {
                        attachment.flip();
                        client.write(ByteBuffer.wrap("this is server".getBytes()));
                    }
                    @Override
                    public void failed(Throwable exc, ByteBuffer attachment) {}}); }@Override
            public void failed(Throwable exc, Object attachment) {}});while (true) {try {
                Thread.sleep(1000);
            } catch(InterruptedException e) { e.printStackTrace(); }}}}Copy the code
Summary: 1 NIO and AIO are implemented with epoll on Linux. 2 AIO is implemented with system implementation on WindowsCopy the code
3 Application Scenarios
1 BIO mode is suitable for a small number of connections and fixed architecture, this mode has high requirements on server resources, concurrency is limited to applications, the only choice before JDK1.4, but the program is intuitive, simple and easy to understand. NIO is suitable for architectures with large number of connections and relatively short (light operation) connections, such as chat servers. Concurrency is limited to applications and programming is complicated. 3 AIO mode is suitable for the architecture with a large number of connections and long connections (heavy operation), such as photo album server, which fully calls the OS to participate in concurrent operations, and the programming is complicated, which is supported by JDK7.Copy the code