【Netty】ByteBuffer原理与使用

Buffer则用来缓冲读写数据,常见的buffer有:

  • ByteBuffer

    • MappedByBuffer

    • DirectByteBuffer

    • HeapByteBuffer

  • hortBuffer

  • IntBuffer

  • LongBuffer

  • FloatBuffer

  • DoubleBuffer

  • CharBuffer


有一个普通文本文件data.txt,内容为:

1234567890abcd

使用fileChannel读取文件内容

package com.aqiuo.buffer;
​
import lombok.extern.slf4j.Slf4j;
​
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
@Slf4j
public class ChannelDemo {
    public static void main(String[] args) throws IOException {
        try (RandomAccessFile file=new RandomAccessFile("data.txt","rw")) {
            FileChannel channel = file.getChannel();
            ByteBuffer buffer = ByteBuffer.allocate(10);
            do {
                int len = channel.read(buffer);
                if (len == -1) {
                    break;
                }
                log.info("得到的字节数{}", len);
                buffer.flip();
                while (buffer.hasRemaining()) {
​
                  log.info("{}",(char) buffer.get());
                }
                buffer.clear();
            }while (true);
        }
    }
}
​

输出

10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - 读到字节数:10
10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - 1
10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - 2
10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - 3
10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - 4
10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - 5
10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - 6
10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - 7
10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - 8
10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - 9
10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - 0
10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - 读到字节数:4
10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - a
10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - b
10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - c
10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - d
10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - 读到字节数:-1

2.1 ByteBuffer 正确使用姿势

  1. 向buffer写入数据,例如调用:channel.read(buffer);

  2. 调用flip切换到读模式

  3. 从buffer读取数据,例如调用buffer.get()

  4. 调用clear()或compact()切换到写模式

  5. 重入1-4步骤

2.2 ByteBuffer的结构

ByteBuffer有以下重要属性:

  • capacity

  • position

  • limit

一开始

写模式下,position是写入位置,limit相当于容量,下图表示写入4字节后的状态。

flip动作发生后,position切换为读取位置,limit切换为读取限制

读取 4 个字节后,状态:

clear动作后,状态:

compact 方法,是把未读完的部分向前压缩,然后切换至写模式

💡 调试工具类
public class ByteBufferUtil {
    private static final char[] BYTE2CHAR = new char[256];
    private static final char[] HEXDUMP_TABLE = new char[256 * 4];
    private static final String[] HEXPADDING = new String[16];
    private static final String[] HEXDUMP_ROWPREFIXES = new String[65536 >>> 4];
    private static final String[] BYTE2HEX = new String[256];
    private static final String[] BYTEPADDING = new String[16];
​
    static {
        final char[] DIGITS = "0123456789abcdef".toCharArray();
        for (int i = 0; i < 256; i++) {
            HEXDUMP_TABLE[i << 1] = DIGITS[i >>> 4 & 0x0F];
            HEXDUMP_TABLE[(i << 1) + 1] = DIGITS[i & 0x0F];
        }
​
        int i;
​
        // Generate the lookup table for hex dump paddings
        for (i = 0; i < HEXPADDING.length; i++) {
            int padding = HEXPADDING.length - i;
            StringBuilder buf = new StringBuilder(padding * 3);
            for (int j = 0; j < padding; j++) {
                buf.append("   ");
            }
            HEXPADDING[i] = buf.toString();
        }
​
        // Generate the lookup table for the start-offset header in each row (up to 64KiB).
        for (i = 0; i < HEXDUMP_ROWPREFIXES.length; i++) {
            StringBuilder buf = new StringBuilder(12);
            buf.append(NEWLINE);
            buf.append(Long.toHexString(i << 4 & 0xFFFFFFFFL | 0x100000000L));
            buf.setCharAt(buf.length() - 9, '|');
            buf.append('|');
            HEXDUMP_ROWPREFIXES[i] = buf.toString();
        }
​
        // Generate the lookup table for byte-to-hex-dump conversion
        for (i = 0; i < BYTE2HEX.length; i++) {
            BYTE2HEX[i] = ' ' + StringUtil.byteToHexStringPadded(i);
        }
​
        // Generate the lookup table for byte dump paddings
        for (i = 0; i < BYTEPADDING.length; i++) {
            int padding = BYTEPADDING.length - i;
            StringBuilder buf = new StringBuilder(padding);
            for (int j = 0; j < padding; j++) {
                buf.append(' ');
            }
            BYTEPADDING[i] = buf.toString();
        }
​
        // Generate the lookup table for byte-to-char conversion
        for (i = 0; i < BYTE2CHAR.length; i++) {
            if (i <= 0x1f || i >= 0x7f) {
                BYTE2CHAR[i] = '.';
            } else {
                BYTE2CHAR[i] = (char) i;
            }
        }
    }
​
    /**
     * 打印所有内容
     * @param buffer
     */
    public static void debugAll(ByteBuffer buffer) {
        int oldlimit = buffer.limit();
        buffer.limit(buffer.capacity());
        StringBuilder origin = new StringBuilder(256);
        appendPrettyHexDump(origin, buffer, 0, buffer.capacity());
        System.out.println("+--------+-------------------- all ------------------------+----------------+");
        System.out.printf("position: [%d], limit: [%d]\n", buffer.position(), oldlimit);
        System.out.println(origin);
        buffer.limit(oldlimit);
    }
​
    /**
     * 打印可读取内容
     * @param buffer
     */
    public static void debugRead(ByteBuffer buffer) {
        StringBuilder builder = new StringBuilder(256);
        appendPrettyHexDump(builder, buffer, buffer.position(), buffer.limit() - buffer.position());
        System.out.println("+--------+-------------------- read -----------------------+----------------+");
        System.out.printf("position: [%d], limit: [%d]\n", buffer.position(), buffer.limit());
        System.out.println(builder);
    }
​
    private static void appendPrettyHexDump(StringBuilder dump, ByteBuffer buf, int offset, int length) {
        if (isOutOfBounds(offset, length, buf.capacity())) {
            throw new IndexOutOfBoundsException(
                    "expected: " + "0 <= offset(" + offset + ") <= offset + length(" + length
                            + ") <= " + "buf.capacity(" + buf.capacity() + ')');
        }
        if (length == 0) {
            return;
        }
        dump.append(
                "         +-------------------------------------------------+" +
                        NEWLINE + "         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |" +
                        NEWLINE + "+--------+-------------------------------------------------+----------------+");
​
        final int startIndex = offset;
        final int fullRows = length >>> 4;
        final int remainder = length & 0xF;
​
        // Dump the rows which have 16 bytes.
        for (int row = 0; row < fullRows; row++) {
            int rowStartIndex = (row << 4) + startIndex;
​
            // Per-row prefix.
            appendHexDumpRowPrefix(dump, row, rowStartIndex);
​
            // Hex dump
            int rowEndIndex = rowStartIndex + 16;
            for (int j = rowStartIndex; j < rowEndIndex; j++) {
                dump.append(BYTE2HEX[getUnsignedByte(buf, j)]);
            }
            dump.append(" |");
​
            // ASCII dump
            for (int j = rowStartIndex; j < rowEndIndex; j++) {
                dump.append(BYTE2CHAR[getUnsignedByte(buf, j)]);
            }
            dump.append('|');
        }
​
        // Dump the last row which has less than 16 bytes.
        if (remainder != 0) {
            int rowStartIndex = (fullRows << 4) + startIndex;
            appendHexDumpRowPrefix(dump, fullRows, rowStartIndex);
​
            // Hex dump
            int rowEndIndex = rowStartIndex + remainder;
            for (int j = rowStartIndex; j < rowEndIndex; j++) {
                dump.append(BYTE2HEX[getUnsignedByte(buf, j)]);
            }
            dump.append(HEXPADDING[remainder]);
            dump.append(" |");
​
            // Ascii dump
            for (int j = rowStartIndex; j < rowEndIndex; j++) {
                dump.append(BYTE2CHAR[getUnsignedByte(buf, j)]);
            }
            dump.append(BYTEPADDING[remainder]);
            dump.append('|');
        }
​
        dump.append(NEWLINE +
                "+--------+-------------------------------------------------+----------------+");
    }
​
    private static void appendHexDumpRowPrefix(StringBuilder dump, int row, int rowStartIndex) {
        if (row < HEXDUMP_ROWPREFIXES.length) {
            dump.append(HEXDUMP_ROWPREFIXES[row]);
        } else {
            dump.append(NEWLINE);
            dump.append(Long.toHexString(rowStartIndex & 0xFFFFFFFFL | 0x100000000L));
            dump.setCharAt(dump.length() - 9, '|');
            dump.append('|');
        }
    }
​
    public static short getUnsignedByte(ByteBuffer buffer, int index) {
        return (short) (buffer.get(index) & 0xFF);
    }
}

2.3 ByteBuffer 常见方法

分配空间

可以使用allocate 方法为ByteBuffer分配空间,其他buffer类也有该方法

Bytebuffer buf = ByteBuffer.allocate(16);
向buffer写入数据

有两种方法:

调用channel的read方法

调用buffer自己的put方法

int readBytes=channel.read(buf);
buf.put((byte)127);
从buffer读取数据

同样有两种办法

  • 调用 channel 的 write 方法

  • 调用 buffer 自己的 get 方法

int writeBytes = channel.write(buf);
byte b = buf.get();

get 方法会让 position 读指针向后走,如果想重复读取数据

  • 可以调用 rewind 方法将 position 重新置为 0

  • 或者调用 get(int i) 方法获取索引 i 的内容,它不会移动读指针

mark 和 reset

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

​
import lombok.extern.slf4j.Slf4j;
​
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
@Slf4j
public class TestByteBuffer {
    public static void main(String[] args) throws IOException {
​
        File file=new File("data.txt");
        FileInputStream fileInputStream=new FileInputStream(file);
        FileChannel fileChannel=fileInputStream.getChannel();
        ByteBuffer buffer=ByteBuffer.allocate(10);
        int len;
​
            len=fileChannel.read(buffer);
            log.info("读取的字节数:"+len);
​
​
            buffer.flip();
        System.out.println((char) buffer.get());
        System.out.println((char)buffer.get());
        System.out.println((char)buffer.get());
        buffer.mark();
        System.out.println((char)buffer.get());
        System.out.println((char)buffer.get());
        System.out.println((char)buffer.get());
        buffer.reset();
        System.out.println((char)buffer.get());System.out.println((char)buffer.get());
        System.out.println((char)buffer.get());
        }
​
    }
19:08:05.220 [main] INFO com.aqiuo.buffer.TestByteBuffer - 读取的字节数:10
1
2
3
4
5
6
4
5
6

注意

rewind 和 flip 都会清除 mark 位置

字符串和ByteBuffer的换转
package com.aqiuo.buffer;
​
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
​
public class TestByteBufferEncode {
    public static void main(String[] args) {
        ByteBuffer buffer1= StandardCharsets.UTF_8.encode("nihao");
        ByteBuffer buffer2= Charset.forName("UTF-8").encode("nihao");
        ByteBufferUtil.debugAll(buffer1);
        ByteBufferUtil.debugAll(buffer2);
        CharBuffer buffer3=StandardCharsets.UTF_8.decode(buffer1);
        System.out.println(buffer3.getClass());
        System.out.println(buffer3.toString());
​
    }
}

输出

+--------+-------------------- all ------------------------+----------------+
position: [0], limit: [5]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 6e 69 68 61 6f                                  |nihao           |
+--------+-------------------------------------------------+----------------+
+--------+-------------------- all ------------------------+----------------+
position: [0], limit: [5]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 6e 69 68 61 6f                                  |nihao           |
+--------+-------------------------------------------------+----------------+
class java.nio.HeapCharBuffer
nihao

⚠️ Buffer 的线程安全

Buffer 是非线程安全的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/715008.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot实战:图书信息网站

实战概述&#xff1a;Spring Boot图书信息网站开发 项目背景 随着数字化时代的到来&#xff0c;图书信息网站为用户提供了一个便捷的在线浏览和购买图书的平台。本实战项目旨在通过Spring Boot框架开发一个图书信息网站&#xff0c;实现图书展示、用户登录和管理等功能。 项…

MySQL数据库(三)

一.MySQL数据库学习(三) (一).数据表的约束 为防止错误的数据被插入到数据表&#xff0c;MySQL中定义了一些维护数据库完整性的规则&#xff1b;这些规则常称为表的约束。 约束条件说明PRIMARY KEY主键约束用于唯一标识对应的记录FOREIGN KEY外键约束NOT NULL非空约束UNIQUE…

牛客 第二十届西南科技大学ACM程序设计竞赛(同步赛):祖玛

题目描述 wzy 在玩一种很新的祖玛。 给定一个仅包含 小写字母 的字符串 sss , sss 由 mmm 个不同的小写字母组成&#xff0c;每个字母代表一种小球&#xff0c;在消去时会获得 相应 的分数&#xff1a; 两个及以上 相同的小球相碰就会消失&#xff08;在发射小球前因为无相碰&…

Excel中多条件判断公式怎么写?

在Excel里&#xff0c;这种情况下的公式怎么写呢&#xff1f; 本题有两个判断条件&#xff0c;按照题设&#xff0c;用IF函数就可以了&#xff0c;这样查看公式时逻辑比较直观&#xff1a; IF(A2>80%, 4, IF(A2>30%, 8*(A2-30%),0)) 用IF函数写公式&#xff0c;特别是当…

Blossom:支持私有部署的云端双链笔记软件分享

Blossom 是一款支持私有部署的云端双链笔记软件&#xff0c;能够帮助用户将笔记、图片和个人计划安排保存在自己的服务器中&#xff0c;并在任意设备之间实时同步。同时&#xff0c;它还可以作为一个动态博客使用。本文将详细介绍 Blossom 的特点和使用方法。 一、Blossom 的特…

使用百度的长文本转语音API时无法下载.MP3文件

今天是学生们交作业的时候&#xff0c;结果是我最忙碌的一天&#xff0c;各种改bug。 有个学生来问&#xff1a; 我在百度提供的API代码(长文本转语音)的基础上添加了下载生成的.MP3文件的代码&#xff0c;运行之后成功建成了.MP3文件&#xff0c;但是文件的内容确实以下的报错…

Git管理(Linux版本)

在Linux中我们如何把自己的代码上传到gitee中呢&#xff0c;本期将为大家讲解详细的步骤。 目录 查看Linux环境是否存在git工具 在gitee上创建代码仓库 复制仓库的HTTP路径到Linux中 代码上传 在仓库下创建文件或者将文件移动到仓库下 使用三板斧进行文件的上传 add …

部署大模型LLM

在autodl上部署大模型 windows运行太麻烦&#xff0c;环境是最大问题。 选择云上服务器【西北B区 / 514机】 cpp (c c plus plus) 纯 C/C 实现&#xff0c;无需外部依赖。针对使用 ARM NEON、Accelerate 和 Metal 框架的 Apple 芯片进行了优化。支持适用于 x86 架构的 AVX、…

FineReport简单介绍(2)

一、报表类型 模板设计是 FineReport 学习过程中的主要难题所在&#xff0c;FineReport 模板设计主要包括普通报表、聚合报表、决策报表三种设计类型。 报表类型简介- FineReport帮助文档 - 全面的报表使用教程和学习资料 二、聚合报表 2-1 介绍 聚合报表指一个报表中包含多个…

Linux,shell ,gun基本概念和关系

Linux 系统简单架构图 1、命令行界面&#xff08;CLI&#xff09;和图形用户界面 (GUI) 1、图形界面就是我们常用的windows系统这种&#xff0c;打开文件&#xff0c;双击一下。想选择哪个文件&#xff0c;就鼠标移动到哪里选择就行。 2、命令行界面就是下面这种只有黑乎乎的…

离散数学--连通性和矩阵

目录 0.关系的运算和性质 1.通路和回路 2.连通关系 3.割点&#xff08;边&#xff09;和点&#xff08;边&#xff09;割集 4.强&#xff08;弱&#xff09;连通&单向连通 0.关系的运算和性质 &#xff08;1&#xff09;这个运算包括了矩阵的运算&#xff0c;包括这个…

vue3实现表格的分页以及确认消息弹窗

表格的分页实例展示 效果1:表格按照每行10条数据分页,且编号也会随之分页自增 实现按照页码分页效果 第二页 展示编号根据分页自动增长 固定表格高度 这边设置了滚动条,同时表格高度实现自适应滚动条高度 template部分 表格代码 编号是按照页码条数进行循环并根据索引自增…

N32G031 ADC初始化

目录 1. ADC初始化概述 2. ADC初始化详细步骤 2.1 ADC配置 2.2 ADC初始化函数调用 2.3 DMA配置&#xff08;可选&#xff09; 3. 初始化结果验证 4. 注意事项 ADC采样注意事项 1. ADC初始化概述 在N32G031单片机中&#xff0c;ADC的初始化是确保ADC模块能够正常工作的…

Python基础用法 之 数据类型

Python常见数据类型分类 数字型非数字型整型&#xff1a; 整数--int--16 字符串&#xff1a;使用引号引起来的的就是字符串--Tom 浮点型&#xff1a;小数--float--16.66列表&#xff1a;list [1,2,3] 布尔型&#xff1a;bool&#xff08;真True&#xff0c;假False&#xff…

c++模板模式

文章目录 模板模式什么是模板模式为什么使用模板模式模板模式实现步骤 示例模板模式优缺点 模板模式 什么是模板模式 模板模式&#xff08;Template Method Pattern&#xff09;是一种行为设计模式&#xff0c;它定义了一个操作中的算法骨架&#xff0c;将某些步骤的具体实现延…

Python(三)---字符串

文章目录 前言1.创建字符串2.字符串的编码3.空字符串和len()函数4.转义字符5.从控制台读取字符串6.字符串的相关操作6.1.通过[]访问元素6.2.字符串切片slice操作6.3.字符串拼接和字符串复制6.4.split()分割和join()合并6.5.常用查找方法6.6.replace() 实现字符串替换6.7.去除首…

Matlab自学笔记三十一:结构数组的创建、索引和预分配内存

1.概念 结构&#xff08;structure array&#xff09;是一种具有容器特性的数据类型&#xff0c;它使用称为字段的数据容器对相关数据进行分组&#xff0c;每个字段可以包含任何类型或大小的数据&#xff0c;所有元素都具有相同数量的字段和相同的字段名称。&#xff08;与元胞…

哈喽GPT-4o——对GPT-4o 提示词的思考与看法

目录 一、提示词二、常用的提示词案例1、写作助理2、改写为小红书风格3、英语翻译和改写4、论文式回答5、主题解构6、提问助手7、Nature风格润色8、结构总结9、编程助手10、充当终端/解释器 大家好&#xff0c;我是哪吒。 最近&#xff0c;ChatGPT在网络上广受欢迎&#xff0c…

gbase8s数据库的逻辑日志、物理日志和两种特殊情形的学习

(一) 日志的介绍 1. 日志的类别 数据库日志主要是分为记录日志、逻辑日志和物理日志。 记录日志&#xff1a;记录日志包括了数据库的报错日志、连接日志、sql执行等信息&#xff0c;这些日志不存储在dbspace上&#xff0c;而是保存在操作系统的文件内逻辑日志和物理日志&…