IO model elaboration
IO model is to use what kind of channel to send and receive data. Java supports three kinds of network programming IO modes: BIO, NIO, AIO
BIO(Blocking IO)
Synchronous blocking model, one client connection corresponds to one processing thread
Disadvantages:
1. Read operation in IO code is blocking operation. If the connection does not do data read and write operation, the thread will block and waste resources
2. If there are many threads, it will lead to too many threads and too much pressure on the server.
Application scenario:
The BIO mode is suitable for a fixed architecture with a small number of connections. This mode requires high server resources, but the program is simple and easy to understand.
BIO code example:
package com.hknetty.bio; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; //Server example public class SocketServer { public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(9000); while (true) { System.out.println("Waiting to connect.."); Socket socket = serverSocket.accept(); //Blocking method System.out.println("There are clients connected.."); new Thread(new Runnable() { @Override public void run() { try { handler(socket); } catch (IOException e) { e.printStackTrace(); } } }).start(); } } private static void handler(Socket socket) throws IOException { System.out.println("thread id = " + Thread.currentThread().getId()); byte[] bytes = new byte[1024]; //Receive client data, block method, block when no data is readable System.out.println("Get ready read. . "); int read = socket.getInputStream().read(bytes); System.out.println("read Complete.."); if (read != -1) { System.out.println("Data received from client:" + new String(bytes, 0, read)); System.out.println("thread id = " + Thread.currentThread().getId()); } socket.getOutputStream().write("HelloClient".getBytes()); socket.getOutputStream().flush(); } }
package com.hknetty.bio; import java.io.IOException; import java.net.Socket; //Client code public class SocketClient { public static void main(String[] args) throws IOException { Socket socket = new Socket("localhost", 9000); //Send data to server socket.getOutputStream().write("HelloServer".getBytes()); socket.getOutputStream().flush(); System.out.println("End of sending data to server"); byte[] bytes = new byte[1024]; //Receive the data returned by the server socket.getInputStream().read(bytes); System.out.println("Receiving data from the server:" + new String(bytes)); socket.close(); } }
NIO(Non Blocking IO)
Synchronization is non blocking. The server implementation mode is that one thread can handle multiple requests (connections). The connection requests sent by the client will be registered on the multiplexer selector, and the multiplexer will process when it polls the connection for IO requests.
The Linux API(select, poll, epoll) is generally used to implement the I/O multiplexing bottom layer. Their differences are as follows:
Application scenario:
NIO mode is applicable to the architecture with large number of connections and short connection (light operation), such as chat server, bullet screen system, communication between servers, complex programming, and JDK 1.4 starts to support
NIO has three core components: channel, buffer and selector
- Channels are similar to streams. Each channel corresponds to a buffer, and the underlying layer of the buffer is an array
- The channel will be registered on the selector, and the selector will hand it over to an idle thread according to the occurrence of channel read-write events
- selector can correspond to one or more threads
- NIO's Buffer and channel can be read or written
NIO code example:
package com.hknetty; 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.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; //Server code public class NIOServer { public static void main(String[] args) throws IOException { // Create a service Socket channel to listen on the local port and set it to non blocking mode ServerSocketChannel ssc = ServerSocketChannel.open(); //It must be configured as non blocking to register on the selector, otherwise an error will be reported. The selector mode itself is non blocking mode ssc.configureBlocking(false); ssc.socket().bind(new InetSocketAddress(9000)); // Create a selector Selector selector = Selector.open(); // Register the ServerSocketChannel to the selector, and the selector is interested in the client accept connection operation ssc.register(selector, SelectionKey.OP_ACCEPT); while (true) { System.out.println("Wait for the event to occur.."); // Polling the key in the monitoring channel, select is blocked, and accept() is also blocked int select = selector.select(); System.out.println("Something happened.."); // There are client requests that are polled and monitored Iterator<SelectionKey> it = selector.selectedKeys().iterator(); while (it.hasNext()) { SelectionKey key = it.next(); //Delete the key processed this time to prevent the next select from being processed repeatedly it.remove(); handle(key); } } } private static void handle(SelectionKey key) throws IOException { if (key.isAcceptable()) { System.out.println("A client connection event occurred.."); ServerSocketChannel ssc = (ServerSocketChannel) key.channel(); //NIO non blocking embodiment: the accept method is blocked here, but because of the connection event, the method will be executed immediately and will not block //After processing the connection request, it will not wait for the client to send data SocketChannel sc = ssc.accept(); sc.configureBlocking(false); //Interested in reading events when listening to Channel through Selector sc.register(key.selector(), SelectionKey.OP_READ); } else if (key.isReadable()) { System.out.println("A client data readable event occurred.."); SocketChannel sc = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); //NIO non blocking embodiment: first, read method will not block, second, this event response model, when the read method is called, there must be an event that the client sends data int len = sc.read(buffer); if (len != -1) { System.out.println("Read to data sent by client:" + new String(buffer.array(), 0, len)); } ByteBuffer bufferToWrite = ByteBuffer.wrap("HelloClient".getBytes()); sc.write(bufferToWrite); key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); } else if (key.isWritable()) { SocketChannel sc = (SocketChannel) key.channel(); System.out.println("write Event"); // NIO event trigger is horizontal trigger // When using Java NIO programming, you need to cancel writing events when there is no data to write out, // Register to write events when data is written out key.interestOps(SelectionKey.OP_READ); //sc.close(); } } }
Detailed analysis of NIO server program:
1. Create a ServerSocketChannel and Selector, and register the ServerSocketChannel to the Selector
2. The selector listens for channel events through the select() method. When the client connects, the selector listens for connection events and gets the selectionKey bound when the ServerSocketChannel is registered
3. selectionKey can get the bound ServerSocketChannel through the channel() method
4. ServerSocketChannel get SocketChannel through accept() method
5. Register the SocketChannel to the Selector and care about the read event
6. After registration, a SelectionKey will be returned, which will be associated with the SocketChannel
7. The selector continues to listen for events through the select() method. When the client sends data to the server, the selector listens for the read event and gets the selectionKey bound when SocketChannel registers
8. selectionKey can get the bound socketChannel through the channel() method
9. Read out the data in socketChannel
10. Write the server data back to the client with socketChannel
Conclusion: the NIO model's selector is like a big manager, which is responsible for listening to various IO events and then transferring them to the back-end thread for processing
NIO is not blocked relative to BIO. The back-end thread of BIO needs to block and wait for the client to write data (such as read method). If the client does not write data, the thread will block. NIO hands over the waiting for the client to the main manager, selector, who polls all registered clients and turns over the event to the back-end thread The back-end thread does not need to do any blocking waiting. It can process the data of client events directly, and finish processing immediately, or return to the thread pool for other client events to continue using. There is also the non blocking of channel reading and writing.
Redis is a typical NIO thread model. The selector collects all connected events and transfers them to the back-end thread. The thread continuously executes all event commands and writes the results back to the client
package com.hknetty.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.util.Iterator; //Client code public class NioClient { //Channel Manager private Selector selector; /** * Start client test * * @throws java.io.IOException */ public static void main(String[] args) throws IOException { NioClient client = new NioClient(); client.initClient("127.0.0.1", 9000); client.connect(); } /** * Obtain a Socket channel, and do some initialization work for the channel* * * @param ip ip of the connected server * @param port Port number of the connected server * @throws IOException */ public void initClient(String ip, int port) throws IOException { // Get a Socket channel SocketChannel channel = SocketChannel.open(); // Set channel to non blocking channel.configureBlocking(false); // Get a channel manager this.selector = Selector.open(); // The client connects to the server. In fact, the implementation of the method does not realize the connection. It needs to be adjusted in the listen() method. // Use channel.finishConnect(); to complete the connection channel.connect(new InetSocketAddress(ip, port)); //Bind the channel manager to the channel and register the selectionkey.op'connect event for the channel. channel.register(selector, SelectionKey.OP_CONNECT); } /** * Use polling to monitor whether there are events to be processed on the selector. If so, process them* * * @throws IOException */ public void connect() throws IOException { // Polling access selector while (true) { selector.select(); // Get the iterator of the selected item in the selector Iterator<SelectionKey> it = this.selector.selectedKeys().iterator(); while (it.hasNext()) { SelectionKey key = (SelectionKey) it.next(); // Delete the selected key to prevent duplicate processing it.remove(); // Connection event occurred if (key.isConnectable()) { SocketChannel channel = (SocketChannel) key.channel(); // If connecting, complete the connection if (channel.isConnectionPending()) { channel.finishConnect(); } // Set to non blocking channel.configureBlocking(false); //You can send information to the server here ByteBuffer buffer = ByteBuffer.wrap("HelloServer".getBytes()); channel.write(buffer); //After the connection with the server is successful, in order to receive the information of the server, you need to set the read permission for the channel. channel.register(this.selector, SelectionKey.OP_READ); // Get readable events } else if (key.isReadable()) { read(key); } } } } /** * Handle the event of reading the information sent by the server * * @param key * @throws IOException */ public void read(SelectionKey key) throws IOException { //Same as the read method of the server // Server readable message: get the Socket channel where the event occurred SocketChannel channel = (SocketChannel) key.channel(); // Create read buffer ByteBuffer buffer = ByteBuffer.allocate(512); int len = channel.read(buffer); if (len != -1) { System.out.println("Client receives message:" + new String(buffer.array(), 0, len)); } } }
AIO(NIO 2.0)
Asynchronous and non blocking. After the operating system completes, the callback informs the server program to start the thread to process. It is generally suitable for applications with a large number of connections and a long connection time
Application scenario:
AIO mode is applicable to the architecture with a large number of connections and a long connection (reoperation). JDK7 starts to support
Example AIO Code:
package com.hknetty.aio; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousServerSocketChannel; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; //Server code public class AIOServer { public static void main(String[] args) throws Exception { final AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(9000)); serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() { @Override public void completed(AsynchronousSocketChannel socketChannel, Object attachment) { try { // If you do not write this line of code, the client can not connect to the server serverChannel.accept(attachment, this); System.out.println(socketChannel.getRemoteAddress()); ByteBuffer buffer = ByteBuffer.allocate(1024); socketChannel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() { @Override public void completed(Integer result, ByteBuffer buffer) { buffer.flip(); System.out.println(new String(buffer.array(), 0, result)); socketChannel.write(ByteBuffer.wrap("HelloClient".getBytes())); } @Override public void failed(Throwable exc, ByteBuffer buffer) { exc.printStackTrace(); } }); } catch (IOException e) { e.printStackTrace(); } } @Override public void failed(Throwable exc, Object attachment) { exc.printStackTrace(); } }); Thread.sleep(Integer.MAX_VALUE); } }
package com.hknetty.aio; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousSocketChannel; //Client code public class AIOClient { public static void main(String... args) throws Exception { AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open(); socketChannel.connect(new InetSocketAddress("127.0.0.1", 9000)).get(); socketChannel.write(ByteBuffer.wrap("HelloServer".getBytes())); ByteBuffer buffer = ByteBuffer.allocate(512); Integer len = socketChannel.read(buffer).get(); if (len != -1) { System.out.println("Client receives message:" + new String(buffer.array(), 0, len)); } } }
Comparison of BIO, NIO and AIO:
Synchronous asynchronous and blocking non blocking (segment)
Lao Zhang likes to drink tea. He doesn't talk nonsense and boil water.
Characters: Lao Zhang, two kettles (ordinary kettles, referred to as kettles; rattling kettles, referred to as rattling kettles).
Lao Zhang put the kettle on the fire and wait for the water to boil. (synchronous blocking)
Lao Zhang feels a little silly
Lao Zhang put the kettle on the fire, went to the living room to watch TV, and from time to time went to the kitchen to see if the water was boiling. (synchronous non blocking)
Lao Zhang felt a little silly, so he became a high-end man and bought a kettle that can play flute. After the water is boiled, it can make a loud noise.
Lao Zhang put the kettle on the fire and wait for the water to boil. (asynchronous blocking)
Lao Zhang doesn't think it's meaningful to wait like this
Lao Zhang put the kettle on the fire and went to the living room to watch TV. Before the kettle rings, he no longer watched it. When it rings, he went to get it. (asynchronous non blocking)
Lao Zhang thinks he's smart.
The so-called synchronous and asynchronous is only for the kettle.
Ordinary kettle, synchronous; ring kettle, asynchronous.
Although they can all work, they can remind Lao Zhang that the water is boiling after the kettle is finished. This is beyond the reach of ordinary kettles.
Synchronization only allows callers to poll themselves (case 2), which makes Lao Zhang inefficient.
The so-called blocking and non blocking is only for Lao Zhang.
Waiting Lao Zhang, blocking; watching TV Lao Zhang, non blocking.