The essence of the I/O model is what kind of channel is used to send and receive data, which largely determines the performance of program communication.
Java supports three network programming models: BIO, NIO, AIO
BIO: synchronization and blocking, the service implementation mode is one connection and one thread, that is, the client has one connection When making a request, the server needs to start a thread for processing.
NIO: Synchronous and non-blocking, the server implementation mode is a thread processing multiple request connections, that is, the requests sent by the client will be registered to the multiplexer and multiplexed The server polls the connection and processes it if there is an I/O request.
AIO: Asynchronous non-blocking, AIO introduces the concept of asynchronous channel, adopts Proactor mode, simplifies program writing, and only starts threads after valid requests. Its characteristic is that the operating system first Notify the server after completion.
The BIO method is suitable for architectures with a relatively small and fixed number of connections. This method is suitable for The server resource requirements are relatively high, and concurrency is limited to applications. It was the only choice before JDK1.4, but the program is simple and easy to understand.
NIO method is suitable for architectures with a large number of connections and relatively short connections (light operations), such as chat servers, barrage systems, inter-server communications, etc. Programming is more complicated, and JDK1.4 starts to support it.
The AIO method is used in architectures with a large number of connections and relatively long connections (heavy operations), such as photo album servers, which fully call the OS to participate in concurrent operations. The programming is relatively complicated, and JDK7 begins to support it
The server starts a ServerSocket;
The client starts the Socket To communicate with the server, by default the server needs to establish a thread for each client to communicate with it;
After the client sends a request, it first consults the server whether there is a thread response, if not It will wait or be rejected;
If there is a response, the client thread will wait for the request to end before continuing execution;
NIO has three core parts: Selector (selector), Channel (channel), and Buffer (buffer).
NIO is buffer-oriented, or block-oriented programming. The data is read into a buffer that it will process later. It can be moved back and forth in the buffer when needed, which increases the flexibility in the processing process. Use It provides a non-blocking, highly scalable network.
HTTP2.0 uses multiplexing technology to allow the same connection to process multiple requests concurrently, and the number of concurrent requests is several orders of magnitude larger than HTTP1.1.
In short, NIO can handle multiple requests with one thread.
BIO processes data in a stream, while NIO processes data in a block. Block I/O is more efficient than stream I/O. /O is much higher;
BIO is blocking, NIO is non-blocking;
BIO operates based on byte stream and character stream , and NIO operates based on Channel and Buffer. Data is always read from the channel to the buffer, or written from the buffer to the channel. Selector is used to monitor multiple channel events (such as connection requests, data arrival, etc.), so a single thread can monitor multiple client channels.
Flow chart description:
Selector corresponding One thread, one thread corresponds to multiple channels (connections);
This picture reflects that there are three channels registered to the selector //program;
Each channel will correspond to a Buffer;
Which channel the program switches to is determined by events, and Event is an important concept;
Selector will switch on each channel according to different events;
Buffer is a memory block, and there is an array at the bottom;
Data is read and written through Buffer, which is the same as BIO. BIO is either an input stream or an output stream, which cannot be bidirectional. However, NIO's Buffer can be read or written, and the flip method is required to switch;
The channel is bidirectional and can return the status of the underlying operating system. For example, Linux, the underlying operating system channel is bidirectional;
The buffer is essentially a memory block that can read and write data. It can be understood as a container object (including an array). This object Provides a set of methods to make working with memory blocks easier, and the buffer object has built-in mechanisms to track and record the buffer's state changes. Channel provides a channel for reading data from files and networks, but the data read or written must go through Buffer.
In NIO, Buffer is a top-level parent class, which is an abstract class.
ByteBuffer, stores byte data in the buffer;
ShortBuffer, stores string data into the buffer;
CharBuffer, stores character data into the buffer;
IntBuffer, stores integers Data to the buffer;
LongBuffer, stores long integer data into the buffer;
DoubleBuffer, stores decimals into the buffer;
FloatBuffer, stores decimals in the buffer;
## introduced when JDK1.4
9. Channel
(5) FileChannel is used for file data reading and writing, DatagramChannel is used for UDP data reading and writing, ServerSocketChannel and SocketChannel are used for TCP data reading and writing.
2. FileChannel
ByteBuffer supports typed put and get, put into What data type is, get should use the corresponding data type to retrieve, otherwise there may be a BufferUnderflowException exception.
You can convert a normal Buffer into a read-only Buffer.
NIO also provides MappedByteBuffer, which allows files to be modified directly in memory (memory outside the heap), and how to synchronize to files is completed by NIO.
NIO also supports reading and writing operations through multiple Buffers (i.e. Buffer arrays), namely Scattering and Gathering.
Java’s NIO , using non-blocking IO mode. You can use one thread to handle multiple client connections, and you will use the Selector.
Selector can detect whether an event occurs on multiple registered channels. If an event occurs, it obtains the event and handles each event accordingly. In this way, only a single thread can be used to manage multiple channels, that is, to manage multiple connections and requests.
Reading and writing will only occur when there are actual read and write events on the connection/channel, which greatly reduces system overhead and eliminates the need to create a thread for each connection. No need to maintain multiple threads.
Avoids the overhead caused by context switching between multiple threads.
package com.nezha.guor.nio;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.*;import java.util.Iterator;public class NioServer { private Selector selector; private ServerSocketChannel serverSocketChannel; private static final int PORT = 8080; public NioServer() { try { //获得选择器 selector = Selector.open(); serverSocketChannel = ServerSocketChannel.open(); //绑定端口 serverSocketChannel.socket().bind(new InetSocketAddress(PORT)); //设置非阻塞模式 serverSocketChannel.configureBlocking(false); //将该ServerSocketChannel 注册到selector serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); }catch (IOException e) { System.out.println("NioServer error:"+e.getMessage()); } } public void listen() { System.out.println("监听线程启动: " + Thread.currentThread().getName()); try { while (true) { int count = selector.select(); if(count > 0) { //遍历得到selectionKey集合 Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); while (iterator.hasNext()) { SelectionKey key = iterator.next(); if(key.isAcceptable()) { SocketChannel sc = serverSocketChannel.accept(); sc.configureBlocking(false); sc.register(selector, SelectionKey.OP_READ); System.out.println(sc.getRemoteAddress() + " 上线 "); } //通道发送read事件,即通道是可读的状态 if(key.isReadable()) { getDataFromChannel(key); } //当前的key 删除,防止重复处理 iterator.remove(); } } else { System.out.println("等待中"); } } }catch (Exception e) { System.out.println("listen error:"+e.getMessage()); } } private void getDataFromChannel(SelectionKey key) { SocketChannel channel = null; try { channel = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); int count = channel.read(buffer); //根据count的值做处理 if(count > 0) { String msg = new String(buffer.array()); System.out.println("来自客户端: " + msg); //向其它的客户端转发消息(排除自己) sendInfoToOtherClients(msg, channel); } }catch (IOException e) { try { System.out.println(channel.getRemoteAddress() + " 离线了"); //取消注册 key.cancel(); }catch (IOException ex) { System.out.println("getDataFromChannel error:"+ex.getMessage()); } }finally { try { channel.close(); }catch (IOException ex) { System.out.println("channel.close() error:"+ex.getMessage()); } } } //转发消息给其它客户(通道) private void sendInfoToOtherClients(String msg, SocketChannel self ) throws IOException{ System.out.println("服务器转发消息中..."); System.out.println("服务器转发数据给客户端线程: " + Thread.currentThread().getName()); //遍历 所有注册到selector 上的 SocketChannel,并排除 self for(SelectionKey key: selector.keys()) { Channel targetChannel = key.channel(); //排除自己 if(targetChannel instanceof SocketChannel && targetChannel != self) { SocketChannel dest = (SocketChannel)targetChannel; //将信息存储到buffer ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes()); //将buffer数据写入通道 dest.write(buffer); } } } public static void main(String[] args) { //创建服务器对象 NioServer nioServer = new NioServer(); nioServer.listen(); }}
package com.nezha.guor.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;import java.util.Scanner;public class NioClient { private final int PORT = 8080; //服务器端口 private Selector selector; private SocketChannel socketChannel; private String username; public NioClient() throws IOException { selector = Selector.open(); socketChannel = socketChannel.open(new InetSocketAddress("127.0.0.1", PORT)); //设置非阻塞 socketChannel.configureBlocking(false); //将channel注册到selector socketChannel.register(selector, SelectionKey.OP_READ); username = socketChannel.getLocalAddress().toString().substring(1); System.out.println(username + " is ok..."); } //向服务器发送消息 public void sendInfo(String info) { info = username + " 说:" + info; try { socketChannel.write(ByteBuffer.wrap(info.getBytes())); }catch (IOException e) { System.out.println("sendInfo error:"+e.getMessage()); } } //读取从服务器端回复的消息 public void readInfo() { try { int readChannels = selector.select(); if(readChannels > 0) { Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); while (iterator.hasNext()) { SelectionKey key = iterator.next(); if(key.isReadable()) { //得到相关的通道 SocketChannel sc = (SocketChannel) key.channel(); //得到一个Buffer ByteBuffer buffer = ByteBuffer.allocate(1024); //读取 sc.read(buffer); //把读到的缓冲区的数据转成字符串 String msg = new String(buffer.array()); System.out.println(msg.trim()); } } iterator.remove(); //删除当前的selectionKey, 防止重复操作 } else { System.out.println("没有可以用的通道..."); } }catch (Exception e) { System.out.println("readInfo error:"+e.getMessage()); } } public static void main(String[] args) throws Exception { NioClient nioClient = new NioClient(); new Thread() { public void run() { while (true) { nioClient.readInfo(); try { Thread.currentThread().sleep(2000); }catch (InterruptedException e) { System.out.println("sleep error:"+e.getMessage()); } } } }.start(); //发送数据给服务器端 Scanner scanner = new Scanner(System.in); while (scanner.hasNextLine()) { nioClient.sendInfo(scanner.nextLine()); } }}
The above is the detailed content of Analyze the usage and characteristics of Java NIO examples. For more information, please follow other related articles on the PHP Chinese website!