首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Netty入门之基础篇

Netty入门之基础篇

作者头像
@派大星
发布2023-06-28 14:24:18
发布2023-06-28 14:24:18
21500
代码可运行
举报
文章被收录于专栏:码上遇见你码上遇见你
运行总次数:0
代码可运行

本次文章主要给大家带来NIO的相关知识点,一些基础的理论知识,巩固大家的对NIO的理解。好了废话不多说直接步入正题。

一:NIO基础

什么是NIO?

顾名思义NIO:non-blocking io 也可称作new io 非阻塞 IO

NIO的三大组件

1. Channel&Buffer

Channel:

  • 基本概念: Channel有点类似有stream,它是读写数据的双向通道,可以将数据读入buffer,也可以将buffer的数据写入Channel,而之前的stream要么是输入要么就是输出,由此可见Channel比stream更为底层

创建的Channel:

  • FileChannel(文件的数据传输通道)
  • DataGramChannel(UDP网络编程时的数据传输通道)
  • SockeChannel(TCP编程时数据传输通道 客户端服务器都可使用)
  • ServerSocketChannel(TCP编程时数据传输通道 专用于服务器)
  • ......

Buffer

  • 基本概念: Buffer则是用来缓冲读写数据的。

常见的Buffer

  • ByteBuffer
    • MappedByteBuffer
    • DirectByteBuffer(直接内存)
    • HeapByteBuffer(堆内存)
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer
  • CharBuffer

2. Selector

  • 多线程版的设计:

⚠️ 多线程版缺点

内存占用较高 线程上下文切换的成本高 只适合连接数少的场景

  • 线程池版的设计

⚠️ 线程池版缺点

阻塞模式下,线程仅能处理一个socket连接 仅适合短链接场景

  • selector版的设计 selector基本概念selector的作用就是配合一个线程来管理多个channel,获取这些channel上发生的事件,这些channel工作在非阻塞模式下,不会让线程吊死在一个channel上。适合连接数特别多,但流量低的场景(low traffic) 大致如图所示:
  • 调用selector的select()会阻塞直到channel发生了

读写就绪事件

  • ,这些事件发生,select方法就会返回这些事件交给thread来处理。
Channel和Buffer的小demo

Channel获取方式(FileChannel的获取)

  1. 通过输入输出流,可以间接的获取,
  2. 通过randomAccessFileByteBuffer获取方式通过静态方法ByteBuffer.allocate(16)
  • TestByteBuffer
代码语言:javascript
代码运行次数:0
运行
复制
@Slf4j
public class TestByteBuffer {

    public static void main(String[] args) {
        // FileChannel
        // 1. 输入输出流, 
        
        // 2. RandomAccessFile
        // RandomAccessFile file = new RandomAccessFile("helloword/data.txt", "rw")
        try (FileChannel channel = new FileInputStream("data.txt").getChannel()) {
            // 准备缓冲区
            ByteBuffer buffer = ByteBuffer.allocate(10);
            while(true) {
                // 从 channel 读取数据,向 buffer 写入
                int len = channel.read(buffer);
                log.debug("读取到的字节数 {}", len);
                if(len == -1) { // 没有内容了
                    break;
                }
                // 打印 buffer 的内容
                buffer.flip(); // 切换至读模式
                while(buffer.hasRemaining()) { // 是否还有剩余未读数据
                    byte b = buffer.get();
                    log.debug("实际字节 {}", (char) b);
                }
                buffer.clear(); // 切换为写模式
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

data.txt的内容是:

代码语言:javascript
代码运行次数:0
运行
复制
1234567890abcd
ByteBuffer 的使用
  1. 向Buffer写入数据,例如调用channel.read(buffer);
  2. 调用读模式flip()
  3. 从buffer读取数据,调用buffer.get();
  4. 调用clear()compact()切换至写模式,后面介绍两种方法的不同之处。
ByteBuffer的结构

ByteBuffer有以下的重要属性:

  • capacity 容量
  • position 位置(位移)
  • limit 限制(等于容量)

ByteBuffer初始化的时候:

写模式下,position是写入位置,limit等于容量,下图是写入了4个字节后的状态。

当发生filp()动作后,position切换为读取位置,limit切换为读取限制。

读取了4个字节后

clear动作发生后,ByteBuffer进行了初始化

compact()方法,是把未读完的部分向前压缩,然后切换到写模式下,如图所示: (这里将clear和compact进行了讲解,大致区别也是如此)

ByteBuffer常见的方法
  • 分配空间 使用allocate方法为ByteBuffer分配空间,其他的Buffer也有该方法。

例如:

代码语言:javascript
代码运行次数:0
运行
复制
ByteBuffer buf = ByteBuffer.allocate(10);
  • 向ByteBuffer写入数据 方法有两种:
    • 调用channelread方法;
    • 调用buffer自己的put方法;

例如:

代码语言:javascript
代码运行次数:0
运行
复制
int readBytes = channel.read(buffer);

buf.put((byte)127);
  • 从buffer读取数据 方法同样有两种:
    1. 调用buffer的get方法;
    2. 调用channelwrite方法;

例如:

代码语言:javascript
代码运行次数:0
运行
复制
int writeBytes = channel.write(buf);

byte b = buf.get()

💡 注意:

buffer的get()方法会让position读指针向后移动,如果想要重复读取数据:

  • 可以调用rewind()方法将position重新置为0;
  • 调用get(int i)获取索引 i 的内容,它不回移动读指针
ByteBuffer的mark和reset

mark是在读取的时候,做一个 标记,即使position发生改变,只要调用 reset方法就能回到mark的位置

💡 注意: rewindflip都会清楚掉mark位置,前者是将position重置为0,后者是切换读模式,切记切记

字符串和ByteBuffer的互转
代码语言:javascript
代码运行次数:0
运行
复制
 /**
         * 1.字符串转化为ByteBuffer
         * 此方法要想将ByteBuffer转换为字符串则需要切换为读模式-使用flip()方法  该方法还是写模式
         */
        ByteBuffer buffer1 = ByteBuffer.allocate(16);
        buffer1.put("hello".getBytes());
        debugAll(buffer1);

        /**
         * 2.字符串转化为ByteBuffer
         */
        ByteBuffer buffer2 = StandardCharsets.UTF_8.encode("hello");
        debugAll(buffer2);

        /**
         * 3.字符串转化为ByteBuffer
         */
        ByteBuffer buffer3 = ByteBuffer.wrap("hello".getBytes());
        debugAll(buffer3);

        /**
         * ByteBuffer 转换为字符串
         */
        String str3 = StandardCharsets.UTF_8.decode(buffer3).toString();
        System.out.println(str3);
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-07-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码上遇见你 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一:NIO基础
    • NIO的三大组件
  • 1. Channel&Buffer
  • 2. Selector
    • Channel和Buffer的小demo
    • ByteBuffer 的使用
    • ByteBuffer的结构
    • ByteBuffer常见的方法
    • ByteBuffer的mark和reset
    • 字符串和ByteBuffer的互转
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档