NIO
NIO是什么?
NIO的全称是New I/O,与之相对应的是Java中传统的BIO 同步阻塞式IO。
NIO和BIO
BIO:传统的同步阻塞
阻塞即当用户线程发出IO请求后,内核会去查看数据是否已经就绪,若未就绪,则用户线程会处于阻塞状态(让出CPU),当数据就绪后,内核会将数据复制到用户线程,并把结果返回给用户线程,同时结束用户线程的阻塞
同步体现在用户线程需要等待数据就绪后才能向后执行(后面的执行依赖于前面的结果)。
服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,线程数量也会增加。(单线程的时候无所谓,并发访问的时候堵塞就会浪费时间,所以一般用线程池创建多个线程处理IO,这将会消耗过多的内存资源,导致服务器变慢甚至崩溃,)
NIO:提供的IO是同步非阻塞IO
非阻塞体现在用户线程发起IO请求后,会直接得到返回结果,即便在数据未就绪的情况下,也能马上得到失败信息。
同步体现在用户线程需要主动去轮询直到发现数据就绪,再主动将数据从内核拷贝到用户线程。
服务器实现模式为多个客户端连接一个服务器线程(IO多路复用),即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时即数据就绪时才启动一个线程进行处理。
BIO | NIO | |
---|---|---|
客户端个数IO线程 | 1:1 | M:1(1个IO线程处理多个客户端连接) |
IO类型(阻塞) | 阻塞IO | 非阻塞IO |
IO类型(同步) | 同步IO | 同步IO(IO多路复用) |
API使用难度 | 简单 | 非常复杂 |
调式难度 | 简单 | 复杂 |
可靠性 | 非常差 | 高 |
吞吐量 | 低 | 高 |
(1)NIO适合处理连接数目特别多,但是连接比较短(轻操作)的场景,Jetty,Mina,ZooKeeper等都是基于java nio实现。
(2)BIO方式适用于连接数目比较小且固定的场景,这种方式对服务器资源要求比较高,并发局限于应用中。
NIO的实现
Nio为了解决上面的问题,采用了selector和channel来构建了一个多路复用的模型,其中selector是单线程来运行的。我们首先将channel注册到selector之 上并且设置设个channel感兴趣的事件,之后selector会不断的去轮询网卡中的事件,一旦检测到channel感兴趣的事件就会记录保存在selector中的一个列 里。用户在调用的时候直接通过selector中的事件可以找到事件对应的channel,拿到channel之后就可以得到内容。
Selector
同步非阻塞式IO,关键是采用了事件驱动的思想来实现了一个多路转换器。
就是多路复用器,可以监听来自多个客户端的IO事件:
A. 若服务端监听到客户端连接请求,便为其建立通信套接字(Chanel),然后返回继续监听,若同时有多个客户端连接请求到来也可以全部收到,依次为它们都建立通信套接字(Chanel)。
B. 若服务端监听到来自已经创建了通信套接字(Chanel)的客户端发送来的数据(说明数据准备就绪),就会调用对应接口处理接收到的数据,若同时有多个客户端发来数据也可以依次进行处理。
C. 监听多个客户端的连接请求和接收数据请求同时还能监听自己什么时候有数据要发送。
总之就是在一个线程中就可以调用多路复用接口(java中是selector)阻塞同时监听来自多个客户端的IO请求,一旦有收到IO请求就调用对应函数处理。
demo服务端代码:
public class NIOServer {
private final static int BUFFER_SIZE = 1024;
private final static int PORT = 52621;
private ServerSocketChannel serverSocketChannel;
private ByteBuffer byteBuffer;
private Selector selector;
public NIOServer(){
try {
init();
} catch (IOException e) {
e.printStackTrace();
}
}
public void init() throws IOException {//初始化
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.bind(new InetSocketAddress(PORT));
selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
byteBuffer = ByteBuffer.allocateDirect(BUFFER_SIZE);
}
private void listen() throws IOException {
while (true){
if (selector.select() == 0){
continue;
}
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()){
SelectionKey key = iterator.next();
if (key.isAcceptable()){
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = ssc.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
replyClient(socketChannel);
}
if (key.isReadable()){
readDataFromClient(key);
}
iterator.remove();
}
}
}
private void readDataFromClient(SelectionKey key) throws IOException {
SocketChannel socketChannel = (SocketChannel) key.channel();
byteBuffer.clear();
int n;
while ((n = socketChannel.read(byteBuffer)) > 0){
byteBuffer.flip();
while (byteBuffer.hasRemaining()){
socketChannel.write(byteBuffer);
}
byteBuffer.clear();
}
if (n < 0){
socketChannel.close();
}
}
private void replyClient(SocketChannel channel) throws IOException {
byteBuffer.clear();
byteBuffer.put("Hello,I am server!".getBytes());
byteBuffer.flip();
channel.write(byteBuffer);
}
public static void main(String[] args) {
try {
new NIOServer().listen();
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端代码:
public class NIOClient {
private final static int BUFFER_SIZE = 1024;
private final static int PORT = 52621;
private SocketChannel socketChannel;
private ByteBuffer byteBuffer;
public void connect() throws IOException {
socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1", PORT));
byteBuffer = ByteBuffer.allocate(BUFFER_SIZE);
receive();
}
private void receive() throws IOException {
while (true){
int n;
byteBuffer.clear();
while ((n = socketChannel.read(byteBuffer)) > 0){
byteBuffer.flip();
System.out.println("从服务器收到消息: " + new String(byteBuffer.array()));
//socketChannel.write(byteBuffer);//发消息
byteBuffer.clear();
}
}
}
public static void main(String[] args) {
try {
new NIOClient().connect();
} catch (IOException e) {
e.printStackTrace();
}
}
}
https://blog.csdn.net/L_BestCoder/article/details/79352022?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control
https://blog.csdn.net/qq_36636154/article/details/99675629
https://www.cnblogs.com/zedosu/p/6666984.html#commentform
Java NIO学习(一)NIO相关概念 https://blog.csdn.net/lhxaiee123/article/details/76252972
Java NIO学习(二)SelectionKey详解 https://blog.csdn.net/lhxaiee123/article/details/76375338
Java NIO学习(三)Selector监听事件+NIO服务器实例 https://blog.csdn.net/lhxaiee123/article/details/76392546?spm=1001.2014.3001.5501