博客
关于我
netty底层——nio知识点 ByteBuffer+Channel+Selector
阅读量:790 次
发布时间:2023-02-15

本文共 3923 字,大约阅读时间需要 13 分钟。

Nio编程入门:从基础到实践

Nio三大组件

在Nio编程中,Channel、Buffer和Selector是核心组件,它们共同构成了Nio的非阻塞I/O模型。

Channel(数据通道)

Channel 是数据在Nio中流动的核心载体。与传统的输入输出流不同,Channel 是双向的数据通道,可以同时进行读写操作。例如,Client可以通过Channel与Server进行数据交互。

Buffer(缓冲区)

在数据传输过程中,数据需要暂时存储。Buffer 作为缓冲区,用于临时存储从Channel读取或写入的数据。常见的Buffer实现是 ByteBuffer。

Selector(选择器)

Selector 的作用是管理多个Channel。当一个Channel发生读写事件时,Selector会通知线程进行处理。这种机制避免了传统线程模型中单个线程处理多个客户端连接的问题,提高了效率。

ByteBuffer详解

ByteBuffer 是Nio中最常用的缓冲对象。它的使用需要遵循特定的步骤和模式。

ByteBuffer的内部结构

ByteBuffer有三个重要属性:

  • Capacity:缓冲区的容量。
  • Position:当前读写位置。
  • Limit:读写限制。

ByteBuffer的切换模式

  • flip():切换为读模式。
  • clear():切换为写模式。
  • compact():将未读取的数据向前移动,切换为写模式。

ByteBuffer的使用步骤

  • 写入数据:channel.read(buffer);
  • 切换读模式:buffer.flip();
  • 读取数据:buffer.get();
  • 切换写模式:buffer.clear();buffer.compact();
  • ByteBuffer的常用方法

    • 读取数据

      • buffer.get();
      • buffer.get(Byte[] byte);
      • buffer.rewind();
      • buffer.make(); + buffer.reset();
    • 写入数据

      • buffer.put();
      • channel.write(buffer);

    ByteBuffer的实际应用

    字符串与ByteBuffer互转

    在实际开发中,经常需要将字符串与ByteBuffer之间进行转换。

    // 字符串转换为ByteBufferByteBuffer buffer = ByteBuffer.allocate(16);buffer.put("hello".getBytes());buffer.flip();// 读取数据System.out.println(buffer.get()); // 输出"h"// ByteBuffer转换为字符串String str = StandardCharsets.UTF_8.decode(buffer).toString();

    ###黏包半包问题处理

    当网络数据传输过程中出现黏包或丢包时,可以通过ByteBuffer进行重新组合。

    public class ByteBufferContestTest {    private static ByteBuffer messageBuffer;    public static void main(String[] args) {        messageBuffer = ByteBuffer.allocate(20);        ByteBuffer buffer = ByteBuffer.allocate(32);        buffer.put("Hello,World\nI'm hushang\nHo".getBytes());        split2(buffer);        buffer.put("w are you?\n".getBytes());        split2(buffer);    }    private static void split2(ByteBuffer buffer) {        buffer.flip();        for (int i = 0; i < buffer.limit(); i++) {            if (buffer.get(i) == '\n') {                int length = i + 1 - buffer.position();                ByteBuffer target = ByteBuffer.allocate(length);                for (int j = 0; j < length; j++) {                    target.put(buffer.get(j));                }                target.flip();                System.out.println(StandardCharsets.UTF_8.decode(target).toString());            }        }        buffer.compact();    }}

    文件编程

    FileChannel操作

    FileChannel可以通过FileInputStream、FileOutputStream或RandomAccessFile获取。

    // 读取文件ByteBuffer buffer = ByteBuffer.allocate(1024);channel.read(buffer);// 写入文件buffer.flip();channel.write(buffer);

    文件传输

    实现文件传输可以使用transferTo()方法。

    long size = channel1.size();long left = size;while (left > 0) {    left -= channel1.transferTo(left, left, channel2);}

    Path和Files操作

    Path和Files类用于处理文件路径和文件操作。

    Path source = Paths.get("D:/aaa/bb...");System.out.println(path.normalize());
    // 创建目录Files.createDirectory(Paths.get("E:/1bbb/测试创建文件夹"));// 拷贝文件Files.copy(Path source, Path target);// 移动文件Files.move(Path source, Path target, StandardCopyOption.ATOMIC_MOVE);// 删除文件Files.delete(Path path);

    网络编程

    阻塞模式

    传统的Socket编程是基于阻塞模式的。

    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.bind(new InetSocketAddress(8080));List
    socketChannelList = new ArrayList<>();while (true) { SocketChannel socketChannel = serverSocketChannel.accept(); socketChannelList.add(socketChannel); System.out.println("客户端成功连接服务器!!!");}

    非阻塞模式

    Nio提供了非阻塞编程模式,避免了单线程处理多个连接的问题。

    Selector selector = Selector.open();selector.register(channel, new ByteBuffer());while (!selector.isClosed()) {    try {        int n = selector.select();        for (int i = 0; i < n; i++) {            SelectionKey key = selector.selectedKeys()[i];            if (key.isReadable()) {                ByteBuffer buffer = (ByteBuffer) key.attachment();                buffer.flip();                int bytesRead = channel.read(buffer);                if (bytesRead > 0) {                    System.out.println("读取了" + bytesRead + "字节数据");                }            }        }    } catch (IOException e) {        e.printStackTrace();    }}

    转载地址:http://opcfk.baihongyu.com/

    你可能感兴趣的文章
    Mysql面试题精选
    查看>>
    MySQL面试题集锦
    查看>>
    mysql面试题:为什么MySQL单表不能超过2000W条数据?
    查看>>
    mysql面试题:创建索引时会不会锁表?
    查看>>
    mysql颠覆实战笔记(八)--mysql的自定义异常处理怎么破
    查看>>
    mysql驱动、durid、mybatis之间的关系
    查看>>
    mysql驱动支持中文_mysql 驱动包-Go语言中文社区
    查看>>
    MySQL高可用切换_(5.9)mysql高可用系列——正常主从切换测试
    查看>>
    MySQL高可用解决方案详解
    查看>>
    MYSQL高可用集群MHA架构
    查看>>
    MySQL高级-MySQL并发参数调整
    查看>>
    MySQL高级-MySQL应用优化
    查看>>
    MySQL高级-MySQL查询缓存优化
    查看>>
    MySQL高级-MySQL锁
    查看>>
    MySQL高级-SQL优化
    查看>>
    MySQL高级-SQL优化步骤
    查看>>
    MySQL高级-内存管理及优化
    查看>>
    MySQL高级-存储过程和函数
    查看>>
    MySQL高级-索引的使用及优化
    查看>>
    MySQL高级-视图
    查看>>