Java Network IO (BIO,NIO,AIO)

Keywords: socket network Programming Java

Java Network IO (BIO,NIO,AIO)

1.BIO

BIO, called Block-IO, is a synchronous blocking communication mode. Stock IO is commonly referred to as BIO. It is a more traditional way of communication with simple mode and convenient use. However, the transmission processing capacity is low, communication time-consuming and depends on network speed.
BIO Design Principle:
The server is responsible for monitoring client requests and creating a new thread for each client for link processing through an Acceptor thread. A typical request-response model. If the number of clients increases, creating and destroying threads frequently will put a lot of pressure on the server. The latter is called pseudo-asynchronous IO, which uses thread pool instead of new threads.

The server provides IP address and port for monitoring. The client connects to the server through three handshakes of TCP. After successful connection, dual playback can communicate through socket (Stock).
Summary: Socket and Server Socket are used to implement socket channel in BIO model. Blocking, synchronization, and connection establishment are time-consuming.

1.1 BIO

BIO server code, responsible for starting services, blocking services, listening to client requests, new thread processing tasks.

/**
 * ServerSocket Server-side code
 * Created by lyyz on 2018/5/24.
 */
public class BioSocketServer {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket socket = null;
        try {
            //socket address
            SocketAddress socketAddress = new InetSocketAddress("127.0.0.1",8765);
            // Create ServerScoket Server
            serverSocket = new ServerSocket();
            // Binding address
            serverSocket.bind(socketAddress);
            // Loop Blocking Waiting for Client Connection
            while(true){
                socket = serverSocket.accept();
                //Create a new thread to execute the client's request and reply to the response
                Thread thread = new Thread(new BioSocketHandler(socket));
                thread.start();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(serverSocket != null){
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            serverSocket = null;
        }
    }
}
/**
 * Specific Processors of Responses
 * Created by lyyz on 2018/5/24.
 */
public class BioSocketHandler implements Runnable {
    private Socket socket;

    public BioSocketHandler(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        BufferedReader in = null;
        PrintWriter out = null;
        try {
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()),true);
            String request;
            request = in.readLine();
            //Receive client requests and respond
            System.out.println(request);
            out.println("Bio Server response data response");
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(in != null){
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(out != null){
                try {
                    out.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if(socket != null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            socket = null;
        }
    }
}
/**
 * Client Code
 * Created by lyyz on 2018/5/24.
 */
public class BioSocketClient {
    public static void main(String[] args) {
        Socket socket =null;
        PrintWriter out = null;
        BufferedReader in = null;
        try {
            // Create Socket
            socket = new Socket();
            SocketAddress socketAddress = new InetSocketAddress("127.0.0.1",8765);
            //Connect servers
            socket.connect(socketAddress);
            out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()),true);
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            //Send a request to the server
            out.println("Bio Client sends request information  request");
            String response;
            //Receive server response data
            response = in.readLine();
            System.out.println(new String(response));
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(in != null){
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(out != null){
                try {
                    out.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if(socket != null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            socket = null;
        }
    }
}

1.2 Pseudo-Asynchronous I/O Programming

To improve this one-connection-one-thread model, we can use thread pools to manage these threads (see the previous article for more information), implement one or more threads to process N client models (but synchronous blocking I/O is still used at the bottom), commonly referred to as the "pseudo-asynchronous I/O model".
The implementation is very simple. We just need to put the new thread into the thread pool management. We just need to change the Server code.

/**
 * Pseudo-asynchronous IO mode
 * ServerSocket Server-side code
 * Created by lyyz on 2018/5/24.
 */
public class Bio2SocketServer {
    public static void main(String[] args) {
        // Create a thread pool to avoid creating a thread every client connection
        ExecutorService executorService = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),100,120, TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(400));
        ServerSocket serverSocket = null;
        Socket socket = null;
        try {
            //socket address
            SocketAddress socketAddress = new InetSocketAddress("127.0.0.1",8765);
            // Create ServerScoket Server
            serverSocket = new ServerSocket();
            // Binding address
            serverSocket.bind(socketAddress);
            // Loop Blocking Waiting for Client Connection
            while(true){
                socket = serverSocket.accept();
                //Create a new thread to execute the client's request and reply to the response
                executorService.execute(new BioSocketHandler(socket));
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(serverSocket != null){
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            serverSocket = null;
        }
    }
}

2.NIO

NIO, also known as Non-Block IO, is a non-blocking synchronous communication mode.
NIO is a big improvement over BIO. The client and server communicate through Channel. NIO can read and write on Channel. These Channels are registered on Selector multiplexers. Selector polls these Channels continuously through a thread. Find Channel ready to perform IO operations.
NIO implements tens of millions of client requests through a thread polling, which is the characteristic of non-blocking NIO.
NIO provides two different socket channel implementations, Socket Channel and Server Socket Channel, corresponding to the Socket and Server Socket in the traditional BIO model.
Both new channels support both blocking and non-blocking modes.
Blocking mode is as simple as traditional support, but its performance and reliability are not good; non-blocking mode is just the opposite.
For low-load, low-concurrent applications, synchronous blocking I/O can be used to improve development speed and better maintainability; for high-load, high-concurrent (network) applications, NIO non-blocking mode should be used to develop.

Buffer: It's an important difference between NIO and BIO. BIO is to write or read data directly into Stream objects. NIO data operations are performed in buffers. The buffer is actually an array. The most common type of Buffer is ByteBuffer, along with Char Buffer, Short Buffer, Int Buffer, Long Buffer, Float Buffer, Double Buffer.
Channel: Unlike streams, channels are bidirectional. NIO can read, write and simultaneously read and write data through Channel. Channels can be divided into two categories: Selectable Channel and File Channel. Both Socket Channel and Server Socket Channel are subclasses of Selectable Channel.
Multiplexer Selector: The basis of NIO programming. Multiplexers provide the ability to select tasks that are ready. Selector polls the Channel registered on it continuously. If a Channel is ready, it will be polled by Selector. Then, through SelectionKey, it can get the set of Channels ready for subsequent IO operations. As long as the server provides a thread responsible for polling the Selector, it can access thousands of clients, which is the great progress of JDK NIO library.

Note: The code here only realizes the function of sending requests from clients and receiving data from servers. The aim is to simplify the code and make it easy to understand. There is complete code in the github source code.
Summary: Socket Channel and Server Socket Channel are used to implement socket channel in NIO model. Non-blocking/blocking, synchronization, avoiding the overhead of three handshakes for TCP connection establishment.
NIO server code, responsible for opening multiplexer, opening channels, registration channels, polling channels, processing channels.

/**
 * Created by lyyz on 2018/5/24.
 */
public class NioSocketServer implements Runnable{
    //1 Multiplexer (manage all channels)
    private Selector selector;
    // Read buffer
    private ByteBuffer readBuffer = ByteBuffer.allocate(1024);

    public NioSocketServer() {
        try {
            // 1. Turn on multiplexer
            selector = Selector.open();
            //2. Open Server Channel (Network Read-Write Channel)
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            // 3. Set the server channel as non-blocking mode, true as blocking mode and false as non-blocking mode.
            serverSocketChannel.configureBlocking(false);
            // 4. Binding ports
            serverSocketChannel.bind(new InetSocketAddress("127.0.0.1",8765));
            // 5 Register Server Channel to Multiplexer
            // SelectionKey.OP_READ: Expresses concern about read data ready events
            // SelectionKey.OP_WRITE: Expresses concern about write data ready events
            // SelectionKey.OP_CONNECT: Expresses concern about socket channel connection completion events
            // SelectionKey.OP_ACCEPT: Expresses concern about the accept event of server-socket channel
            serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
            System.out.println("Server initialization completed-------------");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        while(true){
            try {
                /**
                 * a.select() Blocked to at least one channel ready for your registered event
                 * b.select(long timeOut) Blocked to at least one channel ready for your registered event or timeOut timeout
                 * c.selectNow() Return immediately. If there is no ready channel, return to 0
                 * select The return value of the method represents the number of ready channels.
                 */
                // 1. Multiplexer listening blocking
                selector.select();
                // 2. Result Set Selected by Multiplexer
                Iterator<SelectionKey> selectionKeys = selector.selectedKeys().iterator();
                // 3. Continuous polling
                while (selectionKeys.hasNext()) {
                    // 4. Get a selected key
                    SelectionKey key = selectionKeys.next();
                    // 5. Remove it from the container after acquisition
                    selectionKeys.remove();
                    // 6. Get only valid key s
                    if (!key.isValid()) {
                        continue;
                    }
                    // Blocking state processing
                    if (key.isAcceptable()) {
                        accept(key);
                    }
                    // Readable state processing
                    if (key.isReadable()) {
                        read(key);
                    }
                }
                } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    // Set the block and wait for the Client request. In traditional IO programming, ServerSocket and Socket are used. Server Socket Channel and Socket Channel used in NIO
    private void accept(SelectionKey selectionKey) {
        try {
            // 1. Access Channel Service
            ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
            // 2. Execution of blocking methods
            SocketChannel socketChannel = serverSocketChannel.accept();
            // 3. Set the server channel as non-blocking mode, true as blocking mode and false as non-blocking mode.
            socketChannel.configureBlocking(false);
            // 4. Register channels on multiplexers and set read identifiers
            socketChannel.register(selector, SelectionKey.OP_READ);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private void read(SelectionKey selectionKey) {
        try {
            // 1. Emptying Buffer Data
            readBuffer.clear();
            // 2. Get the channel registered on the multiplexer
            SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
            // 3. Read the data and return it.
            int count = socketChannel.read(readBuffer);
            // 4. Return content - 1 indicates no data
            if (-1 == count) {
                selectionKey.channel().close();
                selectionKey.cancel();
                return ;
            }
            // 5. If there is data, reset operation is performed before reading data.
            readBuffer.flip();
            // 6. Create a bytes array of the corresponding size according to the buffer size to get the value
            byte[] bytes = new byte[readBuffer.remaining()];
            // 7. Receiving Buffer Data
            readBuffer.get(bytes);
            // 8. Print the acquired data
            System.out.println("NIO Server : " + new String(bytes)); // You cannot use bytes.toString()
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new Thread(new NioSocketServer()).start();
    }
}
/**
 * Created by lyyz on 2018/5/24.
 */
public class NioSocketClient {
    public static void main(String[] args) {
        // 1. Create a connection address
        InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 8765);
        // 2. Declare a connection channel
        SocketChannel socketChannel = null;
        // 3. Create a buffer
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        try {
            // 4. Open the Channel
            socketChannel = SocketChannel.open();
            // 5. Connecting servers
            socketChannel.connect(inetSocketAddress);
            while(true){
                // 6. Define a byte array, and then use the system input function:
                byte[] bytes = new byte[1024];
                // 7. Keyboard input data
                System.in.read(bytes);
                // 8. Put the data in the buffer
                byteBuffer.put(bytes);
                // 9. Reset the buffer
                byteBuffer.flip();
                // 10. Write out the data
                socketChannel.write(byteBuffer);
                // 11. Emptying Buffer Data
                byteBuffer.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != socketChannel) {
                try {
                    socketChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

3.AIO

AIO, also known as NIO 2.0, is a non-blocking asynchronous communication mode. On the basis of NIO, a new concept of asynchronous channel is introduced, and the implementation of asynchronous file channel and asynchronous socket channel is provided.
AIO does not use NIO multiplexers, but uses the concept of asynchronous channels. The read and write method return types are Future objects. The Future model is asynchronous, and its core idea is to remove the waiting time of the main function.

Summary: In AIO model, socket channel is implemented by Asynchronous Socket Channel and Asynnous Server Socket Channel. Non-blocking, asynchronous.

/**
 * AIO, Also known as NIO 2.0 is an asynchronous non-blocking communication mode.
 * AIO The concept of asynchronous channel is introduced. Asynchronous Server Socket Channel and Asynnous Socket Channel whose read and write method return value types are Future objects.
 */
public class AioSocketServer {
    private ExecutorService executorService;          // Thread pool
    private AsynchronousChannelGroup threadGroup;      // Channel group
    public AsynchronousServerSocketChannel asynServerSocketChannel;  // Server channel
    public void start(Integer port){
        try {
            // 1. Create a cache pool
            executorService = Executors.newCachedThreadPool();
            // 2. Creating Channel Groups
            threadGroup = AsynchronousChannelGroup.withCachedThreadPool(executorService, 1);
            // 3. Create server channels
            asynServerSocketChannel = AsynchronousServerSocketChannel.open(threadGroup);
            // 4. Binding
            asynServerSocketChannel.bind(new InetSocketAddress(port));
            System.out.println("server start , port : " + port);
            // 5. Waiting for Client Request
            asynServerSocketChannel.accept(this, new AioServerHandler());
            // The server is blocked all the time, and the real environment is running under tomcat, so this line of code is not needed.
            Thread.sleep(Integer.MAX_VALUE);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        AioSocketServer server = new AioSocketServer();
        server.start(8888);
    }
}
public class AioSocketClient  implements Runnable{
    private static Integer PORT = 8888;
    private static String IP_ADDRESS = "127.0.0.1";
    private AsynchronousSocketChannel asynSocketChannel ;
    public AioSocketClient() throws Exception {
        asynSocketChannel = AsynchronousSocketChannel.open();  // Open the channel
    }
    public void connect(){
        asynSocketChannel.connect(new InetSocketAddress(IP_ADDRESS, PORT));  // Creating connections is the same as NIO
    }
    public void write(String request){
        try {
            asynSocketChannel.write(ByteBuffer.wrap(request.getBytes())).get();
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            asynSocketChannel.read(byteBuffer).get();
            byteBuffer.flip();
            byte[] respByte = new byte[byteBuffer.remaining()];
            byteBuffer.get(respByte); // Put buffer data into byte arrays
            System.out.println(new String(respByte,"utf-8").trim());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    @Override
    public void run() {
        while(true){
        }
    }
    public static void main(String[] args) throws Exception {
        for (int i = 0; i < 10; i++) {
            AioSocketClient myClient = new AioSocketClient();
            myClient.connect();
            new Thread(myClient, "myClient").start();
            myClient.write("aaaaaaaaaaaaaaaaaaaaaa");
        }
    }
}
public class AioServerHandler  implements CompletionHandler<AsynchronousSocketChannel, AioSocketServer> {
    private final Integer BUFFER_SIZE = 1024;
    @Override
    public void completed(AsynchronousSocketChannel asynSocketChannel, AioSocketServer attachment) {
        // Ensure that multiple clients can block
        attachment.asynServerSocketChannel.accept(attachment, this);
        read(asynSocketChannel);
    }
    //Read data
    private void read(final AsynchronousSocketChannel asynSocketChannel) {
        ByteBuffer byteBuffer = ByteBuffer.allocate(BUFFER_SIZE);
        asynSocketChannel.read(byteBuffer, byteBuffer, new CompletionHandler<Integer, ByteBuffer>() {
            @Override
            public void completed(Integer resultSize, ByteBuffer attachment) {
                //After reading, reset the identifier bit
                attachment.flip();
                //Get the number of bytes read
                System.out.println("Server -> " + "The length of data received from the client is:" + resultSize);
                //Get the read data
                String resultData = new String(attachment.array()).trim();
                System.out.println("Server -> " + "The data information received from the client is:" + resultData);
                String response = "Server response, Received data from client: " + resultData;
                write(asynSocketChannel, response);
            }
            @Override
            public void failed(Throwable exc, ByteBuffer attachment) {
                exc.printStackTrace();
            }
        });
    }
    // Write data
    private void write(AsynchronousSocketChannel asynSocketChannel, String response) {
        try {
            // Write the data into the buffer
            ByteBuffer buf = ByteBuffer.allocate(BUFFER_SIZE);
            buf.put(response.getBytes());
            buf.flip();
            // Write from buffer to channel
            asynSocketChannel.write(buf).get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
    @Override
    public void failed(Throwable exc, AioSocketServer attachment) {
        exc.printStackTrace();
    }
}

Reference to this blog
https://blog.csdn.net/anxpp/article/details/51512200
https://segmentfault.com/a/1190000012976683

Posted by Orkan on Sun, 06 Jan 2019 18:09:10 -0800