NIO原理详解

本文深入解析NIO服务器的工作机制,重点介绍了如何使用Selector监听ServerSocketChannel和客户端SocketChannel的事件,包括接受新连接和读取数据的过程。通过具体代码示例,详细展示了注册、选择和处理事件的流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

public class NIOServer {

    /**
     * 代码中register方法源码分析
     * 1,首先判断该SocketChannel中的所有SelectionKey是否有包含该selector的SelectionKey如果有直接操作该SelectionKey
     * 2,如果没有找到该SelectionKey则调用Selector的register方法进行注册
     * 2.1,在Selector的register方法中创建SelectionKeyImpl实例对象,
     * 2.1.1,SelectionKeyImpl对象的作用就是channel与selector对象的绑定关系
     * 2.2,在selector的register方法中将产生的SelectionKey绑定至selector
     * 2.3,在selector的register方法中最后返回一个SelectionKey,将这个SelectionKey绑定至了channel
     * 总结,本质上就是根据channel与selector对象创建一个SelectionKey对象分别绑定到channel与selector上
     */


    /**
     * NIO需要注意的是首先将ServerSocketChannel和关注的事件类型SelectionKey.OP_ACCEPT注册到selector上了,
     * 当循环到走到isAcceptable()的分支时,其实就是循环到了ServerSocketChannel本身,
     * 然后会调用ServerSocketChannel的accept看其是否有接到客户端SocketChannel,
     * 如果有把客户端SocketChannel也注册到selector上生成新的SelectionKey。
     * 也就是说ServerSocketChannel和客户端SocketChannel都会注册到同一selector上。
     * 对于selector来说ServerSocketChannel和SocketChannel都一样只是根据感兴趣的事件类型走不同的分支而已
     */


    @SuppressWarnings("unused")
    public static void main(String[] args) throws IOException {

        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();//创建服务端
        InetSocketAddress serverConfig = new InetSocketAddress("localhost", 1111);//创建服务端参数

        serverSocketChannel.bind(serverConfig);//绑定参数
        serverSocketChannel.configureBlocking(false);//将服务端设置为非阻塞

        Selector selector = Selector.open(); // 创建轮询器
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);//将轮询器和类型都注册指服务器

        while (true) {
            int waitQty = selector.select();//获取轮询到的事件个数
            if (waitQty < 1) continue;//如果没有就进入下一次循环

            Set<SelectionKey> selectionKeys = selector.selectedKeys();//获取轮询器中所有的SelectionKey
            Iterator<SelectionKey> selectionKeyIterator = selectionKeys.iterator();

            while (selectionKeyIterator.hasNext()) {
                SelectionKey currentSelectionKey = selectionKeyIterator.next();//获取链接集合中的一个链接

                if (currentSelectionKey.isAcceptable()) {
                    SocketChannel clientSocketChannel = serverSocketChannel.accept();

                    // 设置客户端为非阻塞
                    clientSocketChannel.configureBlocking(false);

                    //将接到的客户端连接SocketChannel注册到selector上
                    clientSocketChannel.register(selector, SelectionKey.OP_READ);
                    System.out.println("接收了新连接: " + clientSocketChannel.getLocalAddress() + "\n");
                } else if (currentSelectionKey.isReadable()) {
                    //获取SelectionKey中的SocketChannel
                    SocketChannel clientSocketChannel = (SocketChannel) currentSelectionKey.channel();
                    //将客户端发过来的消息读入到buffer中
                    ByteBuffer buffer = ByteBuffer.allocate(256);
                    clientSocketChannel.read(buffer);

                    //将buffer中的数据转换为字符串
                    String result = new String(buffer.array()).trim();

                    System.out.println("接收信息: " + result);
                    if (result.equals("haha")) {
                        clientSocketChannel.close();
                        System.out.println("\n信息为haha则关闭连接");
                        System.out.println("\n服务器将继续运行。尝试再次运行客户端以建立新连接");
                    }
                }
                selectionKeyIterator.remove();
            }
        }
    }

}

如果有理解不对的地方请大家多指正交流。qq17133033

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值