【网络编程(二)】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 封装好后就帮我们解决了这种问题,不会出现事件处理完后续还会一直处理的现象。

相关推荐
拾忆,想起8 分钟前
超时重传 vs 快速重传:TCP双保险如何拯救网络丢包?
java·开发语言·网络·数据库·网络协议·tcp/ip·php
2021_fc21 分钟前
WebSocket技术分享
网络·websocket·网络协议
zizisuo37 分钟前
为什么TCP设计中要设计ACK不重传?
网络·网络协议·tcp/ip
偶像你挑的噻1 小时前
Linux应用开发-17-套接字
linux·网络·stm32·嵌入式硬件
AI分享猿1 小时前
小白学规则编写:雷池 WAF 配置教程,用 Nginx 护住 WordPress 博客
java·网络·nginx
AORO20252 小时前
遨游科普:三防平板是指哪三防?有哪些应用场景?
大数据·网络·5g·智能手机·电脑·信息与通信
鸢尾掠地平2 小时前
DNS的正向、反向解析的服务配置知识点及实验
运维·服务器·网络
草莓熊Lotso2 小时前
C++ 方向 Web 自动化测试实战:以博客系统为例,从用例到报告全流程解析
前端·网络·c++·人工智能·后端·python·功能测试
GhostGuardian2 小时前
DNS报文结构全解析
网络·网络协议
宁雨桥3 小时前
WebSocket 完全指南:从原理到实战,搭建实时通信桥梁
网络·websocket·网络协议