NIO理解

NIO

NIO是什么?

​ NIO的全称是New I/O,与之相对应的是Java中传统的BIO 同步阻塞式IO。

NIO和BIO

BIO:传统的同步阻塞

阻塞即当用户线程发出IO请求后,内核会去查看数据是否已经就绪,若未就绪,则用户线程会处于阻塞状态(让出CPU),当数据就绪后,内核会将数据复制到用户线程,并把结果返回给用户线程,同时结束用户线程的阻塞

同步体现在用户线程需要等待数据就绪后才能向后执行(后面的执行依赖于前面的结果)。

服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,线程数量也会增加。(单线程的时候无所谓,并发访问的时候堵塞就会浪费时间,所以一般用线程池创建多个线程处理IO,这将会消耗过多的内存资源,导致服务器变慢甚至崩溃,)

NIO:提供的IO是同步非阻塞IO

非阻塞体现在用户线程发起IO请求后,会直接得到返回结果,即便在数据未就绪的情况下,也能马上得到失败信息。

同步体现在用户线程需要主动去轮询直到发现数据就绪,再主动将数据从内核拷贝到用户线程。

服务器实现模式为多个客户端连接一个服务器线程(IO多路复用),即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时即数据就绪时才启动一个线程进行处理。

BIONIO
客户端个数IO线程1:1M:1(1个IO线程处理多个客户端连接)
IO类型(阻塞)阻塞IO非阻塞IO
IO类型(同步)同步IO同步IO(IO多路复用)
API使用难度简单非常复杂
调式难度简单复杂
可靠性非常差
吞吐量

(1)NIO适合处理连接数目特别多,但是连接比较短(轻操作)的场景,Jetty,Mina,ZooKeeper等都是基于java nio实现。

(2)BIO方式适用于连接数目比较小且固定的场景,这种方式对服务器资源要求比较高,并发局限于应用中。

NIO的实现

img

img

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值