Netty 学习笔记(二)—— NIO 网络编程

NIO 网络编程(通信)学习笔记

演示

第一阶段

既然是通信,那必然有客户端和服务端
要进行 NIO 的网络通信,首先要创建一个服务端,如下:

//1. 打开 serverSocketChannel 通道并绑定端口
System.out.println("open serverSocketChannel ...");
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8000));

// 保存所有客户端链接
ArrayList<SocketChannel> socketChannelList = new ArrayList<>();
// 保存客户端传来的数据
ByteBuffer buffer = ByteBuffer.allocate(20);

while (true) {
    System.out.println("wait for connect ...");
    //2. 接受客户端链接,会阻塞
    SocketChannel channel = serverSocketChannel.accept();
    socketChannelList.add(channel);
    System.out.println(channel.toString() + " connected");

    //3. 遍历客户端链接,获取数据
    for (SocketChannel socketChannel : socketChannelList) {
        System.out.println("read data ...");
        socketChannel.read(buffer);
        buffer.flip();
        //4. 数据处理
        System.out.println("StandardCharsets.UTF_8.decode(buffer) = " + StandardCharsets.UTF_8.decode(buffer));
        buffer.clear();
    }
}

然后创建客户端

SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress(8000));
// encode 方法生成的 buffer 会切换到 读模式
socketChannel.write(StandardCharsets.UTF_8.encode("hello nio"));
System.out.println("wait other operation ...");

在上述服务端的代码中,有两处阻塞

  1. 连接阻塞 --> accept 方法 --> ServerSocketChannel
  2. IO 阻塞 --> read 方法 --> SocketChannel
第二阶段

要解决阻塞的问题,我们可以做以下处理

serverSocketChannel.configureBlocking(false);
channel.configureBlocking(false);

配置之后会发现,不再会有阻塞发生
但缺点也很明显,因为即使没有任何的客户端连接,也会一直死循环处理,导致 cpu 的空转,造成资源浪费
那么这时候需要引入一个“管理者”,让程序在有链接或数据进来的时候,再去处理

第三阶段
  1. 谁来做管理者? Selector
  2. 管理哪些东西? accept(serverSocketChannel) read(socketChannel) write(socketChannel)
  3. 怎么管理?
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8000));
// selector 只有在 非阻塞模式 下才能使用
serverSocketChannel.configureBlocking(false);

Selector selector = Selector.open();
// 将 serverSocketChannel 注册到 selector 的 keys 中
System.out.println("serverSocketChannel: " + serverSocketChannel);
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT, null);

while (true) {
    // 阻塞,等待 keys 中的 channel 发生特定事件
    System.out.println("select ...");
    selector.select();

    // 每当发生事件时,都会把相应的 key 放到 selectedKeys 中
    Set<SelectionKey> selectionKeys = selector.selectedKeys();
    Iterator<SelectionKey> selectionKeyIterator = selectionKeys.iterator();
    if (selectionKeyIterator.hasNext()) {
        SelectionKey selectionKey = selectionKeyIterator.next();
        // 当前 key 的事件处理完后从 selectedKeys 中删掉
        selectionKeyIterator.remove();

        if (selectionKey.isAcceptable()) {
            System.out.println("isAcceptable ...");
            SocketChannel socketChannel = serverSocketChannel.accept();
            System.out.println("socketChannel: " + socketChannel);
            socketChannel.configureBlocking(false);
            socketChannel.register(selector, SelectionKey.OP_READ, null);
        } else if (selectionKey.isReadable()) {
            System.out.println("isReadable ...");
            SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
            ByteBuffer buffer = ByteBuffer.allocate(20);
            socketChannel.read(buffer);
            buffer.flip();

            System.out.println("StandardCharsets.UTF_8.decode(buffer) = " + StandardCharsets.UTF_8.decode(buffer));
        }
    }
}

selector 中

  1. keys 存储所有注册到 selector 中的 channel 对应的 key
  2. selectedKeys 存储 keys 中当前有特定(注册时指定)事件发生的 channel 对应的 key

结论

  1. NIO 不是没有阻塞,ServerSocketChannel 的 accept 方法和 SocketChannel 的 read 方法都会导致阻塞
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值