BIO&NIO&AIO&Netty 初识二 NIO

NIO

入门代码

nio服务端代码,可以设置为非阻塞,每个socketchannel表示一个客户端连接的管道,发到一个集合中,循环获取客户端发送的消息
缺点:

  1. 如果连接太多,集合会越来越多
  2. 如果集合太多,但实际发送消息的客户端很少,每次全部循环,性能损耗大
public class NioServer {
    public static void main(String[] args) throws IOException {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // 绑定9000端口
        serverSocketChannel.socket().bind(new InetSocketAddress(9000));
        // 设置成非阻塞默认是true
        serverSocketChannel.configureBlocking(false);
        List<SocketChannel> clientChannelList = new ArrayList<>();
        while (true) {
            SocketChannel clientChannel = serverSocketChannel.accept();
            if (clientChannel != null) {
                // 设置成非阻塞
                clientChannel.configureBlocking(false);
                clientChannelList.add(clientChannel);
            }
            for (SocketChannel socketChannel : clientChannelList) {
                ByteBuffer byteBuffer = ByteBuffer.allocate(128);
                // 读取客户端发送的消息(目前是非阻塞)
                int len = socketChannel.read(byteBuffer);
                if (len > 0) {
                    System.out.println("收到客户端消息:" + new String(byteBuffer.array()));
                }
            }
        }
    }
}

优化版本

使用Selector多路复用器,监听指定事件,发生事件时会放到单独的集合中,这样循环就只循环有事件发生的集合就行

public class NioSelectorServer {

    public static void main(String[] args) throws IOException {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // 绑定9000端口
        serverSocketChannel.socket().bind(new InetSocketAddress(9000));
        // 设置成非阻塞默认是true
        serverSocketChannel.configureBlocking(false);
        // 多路复用器
        Selector selector = Selector.open();
        // 多路复用器上监听连接事件,发生连接会发到一个selector的集合中
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        while (true) {
            // 阻塞直到有事件发生
            selector.select();
            // 发生的事件
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            // 递归事件
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                // 获取当前事件
                SelectionKey selectionKey = iterator.next();
                if (selectionKey.isAcceptable()) {
                    // 如果是连接事件,获取对应的连接管道
                    ServerSocketChannel clientChannel = (ServerSocketChannel)selectionKey.channel();
                    SocketChannel socketChannel = clientChannel.accept();
                    // 设置为非阻塞模式
                    socketChannel.configureBlocking(false);
                    // 监听读事件(客户端发送数据)
                    socketChannel.register(selector, SelectionKey.OP_READ);
                } else if (selectionKey.isReadable()) {
                    // 如果客户端发送请求,获取对应的连接管道
                    SocketChannel socketChannel = (SocketChannel)selectionKey.channel();
                    ByteBuffer byteBuffer = ByteBuffer.allocate(128);
                    // 读取客户端发送的消息(目前是非阻塞)
                    int len = socketChannel.read(byteBuffer);
                    if (len > 0) {
                        System.out.println("收到客户端消息:" + new String(byteBuffer.array()));
                    }
                }
                // 处理完的事件,移出
                iterator.remove();
            }

        }
    }
}

源码

jdk1.4

nio使用linux的select()或poll()来实现,这两个方法与上面写的入门代码类似,都是循环所有连接,判断客户端是否发送数据。select()对客户端连接数有限制(好像只支持1024个连接),poll()对客户端连接数没有限制

jdk1.5

nio使用linux的epoll实现,epoll基于事件监听机制,可以监听指定事件,如果发生该事件,放到指定集合中,这样只需要遍历这个发生过事件的集合就行

epoll函数

java调用selector的方法时,底层实际使用linux的epoll实现
epoll利用操作系统的中断实现事件监听
BIO&NIO&AIO&Netty 初识二 NIO

redis底层

redis底层就是使用epoll实现多路复用器,监听客户端不同事件(连接,发送数据等…)

上一篇:【Python网络编程】epoll用法


下一篇:Redis---- 小结(原创笔记)