Catalog
4. Comparison of bio, NIO and AIO:
Java supports three kinds of network programming IO modes: BIO, NIO, AIO. The IO model is to use what kind of channel to send and receive data,
1. 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: https://github.com/ssy-githup/io-mode Under the bio package of
//Server code: /** * bio Server in mode */ public class SocketServer { public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(9000); while (true) { System.out.println("Waiting to connect.."); //Blocking method final Socket socket = serverSocket.accept(); System.out.println("There is a client connected.."); new Thread(new Runnable() { @Override public void run() { try { handler(socket); } catch (IOException e) { e.printStackTrace(); } } }).start(); //handler(socket); } } private static void handler(Socket socket) throws IOException { System.out.println("Current thread ID= " + Thread.currentThread().getId()); byte[] bytes = new byte[1024]; System.out.println("Get ready read. . "); //Receive client data, block method, block when no data is readable 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("Current thread ID = " + Thread.currentThread().getId()); } socket.getOutputStream().write("HelloClient".getBytes()); socket.getOutputStream().flush(); } }
Client code:
/** * bio Client */ public class SocketClient { public static void main(String[] args) throws IOException { Socket socket = new Socket("127.0.0.1", 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("Data received from the server:" + new String(bytes)); socket.close(); } }
1.2 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:
select | poll | epoll(jdk 1.5 and above) | |
Operation mode |
ergodic |
ergodic |
Callback |
Bottom implementation |
array |
Linked list |
Hashtable |
IO efficiency |
Each call is traversed linearly with a time complexity of O(n) |
Each call is traversed linearly with a time complexity of O(n) |
Event notification mode: whenever an IO event is ready, the callback function registered by the system will be called, time complexity O(1) |
Maximum connection |
Upper limit |
No upper limit |
No upper limit |
Application scenario:
NIO mode is applicable to the architecture with a large number of connections and a short connection (light operation), such as chat server, bullet screen system, communication between servers, complex programming, JDK 1.4 started to support
NIO has three core components: channel, buffer and selector
1. Channels are similar to streams. Each channel corresponds to a buffer, and the underlying layer of the buffer is an array
2. 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
3. selector can correspond to one or more threads
4. NIO's Buffer and channel can be read or written
NIO code example: 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(8888)); // 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, the client will not continue to wait for the data to be sent 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(); } } }
Client code:
public class NioClient { //Channel Manager private Selector selector; /** * Start client test * * @throws IOException */ public static void main(String[] args) throws IOException { NioClient client = new NioClient(); client.initClient("127.0.0.1", 8888); 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(1024); int len = channel.read(buffer); if (len != -1) { System.out.println("Client received message:" + new String(buffer.array(), 0, len)); } } }
NIO server code flow:
- Create a ServerSocketChannel and Selector, and register the ServerSocketChannel to the Selector
- 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
- selectionKey can get the bound ServerSocketChannel through the channel() method
- ServerSocketChannel get SocketChannel through accept() method
- Register the SocketChannel to the Selector and care about the read event
- After registration, a SelectionKey will be returned, which will be associated with the SocketChannel
- 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
- selectionKey can get the bound socketChannel through the channel() method
- Read out the data in socketChannel
- Write the server data back to the client with socketChannel
Conclusion:
The NIO model's selector is like a general 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 gives the task of waiting for the client's operation to the big manager, selector It is responsible for polling all registered clients. Only when an event occurs can it be transferred to the back-end thread for processing. The back-end thread does not need to do any blocking and waiting. It can directly process the client event data. After processing, it will end immediately, or return to the thread pool for other client events to continue to use. And the channel read and write are non blocking
3. 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
4. Comparison of bio, NIO and AIO:
BIO | NIO | AIO | |
IO model | Synchronous block | Synchronous non blocking | Asynchronous non blocking |
Code difficult | simple | complex | complex |
reliability | difference | good | good |
throughput | low | high | high |
Conclusion:
Use the fishing scene to compare the io model:
BIO:
Pseudo asynchronous IO: I
NIO:
AIO: