【网络编程(二)】NIO快速入门

NIO

Java NIO 三大核心组件

  1. Buffer(缓冲区):每个客户端连接都会对应一个Buffer,读写数据通过缓冲区读写。
  2. Channel(通道):每个channel用于连接Buffer和Selector,通道可以进行双向读写。
  3. Selector(选择器):一个选择器对应多个通道,用于监听多个通道的事件。Selector可以监听所有的channel是否有数据要读取,当某个channel有数据时,就去处理,所有channel都没有数据时,线程可以去执行其他任务。

使用 NIO 模型操作 Socket 步骤:

  1. 创建 ServerSocketChannel 服务器;
  2. 创建多路复用器 Selector(每个操作系统创建出来的是不一样的 ,Windows创建的是 WindowsSelectorImpl)
  3. ServerSocketChannel 将建立连接事件注册到 Selector中(register 方法往 EPollArrayWrapper 中添加元素)
  4. 处理事件
    1. 如果是建立连接事件,则把客户端的读写请求也注册到Selector中;
    2. 如果是读写事件则按业务处理。

案例代码:

java 复制代码
public class NioServer {

    public static void main(String[] args) throws IOException {
        // 创建服务器
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(8888));
        serverSocketChannel.configureBlocking(false); // 配置成非阻塞式的channel
        // 创建一个IO多路复用选择器
        Selector selector = Selector.open();
        // 注册
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            // 阻塞的方法,返回值代表发生事件的通道的个数
            // 返回值 0 超时
            // -1 错误
            // select方法可以传递超时时间,如果不传的话是timeout最后会为-1表示不会超时
            selector.select();// 如果不设的话客户端不操作会一直阻塞在这
            // 只要走到这里,必然说明,发送了事情,有可读可写可连接的channel
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                // 这个事件处理完就删除
                if (selectionKey.isAcceptable()) {
                    // 有客户端来连接了
                    // 三次握手建立连接
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                    System.out.println("某某客户端连接来啦");
                }

                if (selectionKey.isReadable()) {
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                    ByteBuffer buffer = (ByteBuffer) selectionKey.attachment();
                    buffer.clear();
                    int read = socketChannel.read(buffer);
                    if (read == -1) { // 如果是可读事件,然后又没有数据,说明是客户端与服务器端的连接断开了,
                        // 这个时候我们关闭通道,不然选择器会一直监听通道,导致不必要的业务执行
                        socketChannel.close();
                    } else {
                        System.out.println(new String(buffer.array(), 0, buffer.position()));
                        System.out.println("有信息需要读取");
                    }
                }
                iterator.remove();
            }
        }

    }
}

doSelect 方法是由 WindowsSelectorImpl 类去实现的,这是select方法最后执行的方法,因为加了互斥锁,也是为什么说这里同步阻塞的原因。

俩问题:

  • 当 Selector.select() 方法返回后,它会返回一组 SelectionKey 对象,这些对象代表了已经就绪的 I/O 通道,即对应的文件描述符上有事件发生。这些 SelectionKey 对象Key用来处理对应的事件。但是,如果不将已经处理过的 SelectionKey 对象从 Selector 中删除,下次调用 Selector.select() 方法时,这些已经处理过的 SelectionKey 对象扔然会被返回,导致多余的事件处理,影响性能问题。

  • 删除的话,我们可以通过 SelectionKey.cancel() 方法来实现,并且使得对应的通道(即文件描述符)不再被Selector监视。(这是有问题的,这样的话以后这个 SelectionKey 就不会再被监听了)可以在迭代器使用的时候对其进行删除。

Netty 封装好后就帮我们解决了这种问题,不会出现事件处理完后续还会一直处理的现象。

相关推荐
SZ17011023122 分钟前
IP协议 标识字段 同一个源IP、目的IP和协议号内唯一
网络·网络协议·tcp/ip
狐572 小时前
2025-06-02-IP 地址规划及案例分析
网络·网络协议·tcp/ip
黎茗Dawn2 小时前
5.子网划分及分片相关计算
网络·智能路由器
恰薯条的屑海鸥3 小时前
零基础在实践中学习网络安全-皮卡丘靶场(第十四期-XXE模块)
网络·学习·安全·web安全·渗透测试
科技小E3 小时前
口罩佩戴检测算法AI智能分析网关V4工厂/工业等多场景守护公共卫生安全
网络·人工智能
御承扬3 小时前
从零开始开发纯血鸿蒙应用之网络检测
网络·华为·harmonyos
DevSecOps选型指南11 小时前
2025软件供应链安全最佳实践︱证券DevSecOps下供应链与开源治理实践
网络·安全·web安全·开源·代码审计·软件供应链安全
国科安芯12 小时前
抗辐照MCU在卫星载荷电机控制器中的实践探索
网络·嵌入式硬件·硬件工程·智能硬件·空间计算
EasyDSS14 小时前
国标GB28181设备管理软件EasyGBS远程视频监控方案助力高效安全运营
网络·人工智能
玩转4G物联网14 小时前
零基础玩转物联网-串口转以太网模块如何快速实现与TCP服务器通信
服务器·网络·物联网·网络协议·tcp/ip·http·fs100p