【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 paddingsfor (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 conversionfor (i = 0; i < BYTE2HEX.length; i++) {BYTE2HEX[i] = ' ' + StringUtil.byteToHexStringPadded(i);}
​// Generate the lookup table for byte dump paddingsfor (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 conversionfor (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 dumpint rowEndIndex = rowStartIndex + 16;for (int j = rowStartIndex; j < rowEndIndex; j++) {dump.append(BYTE2HEX[getUnsignedByte(buf, j)]);}dump.append(" |");
​// ASCII dumpfor (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 dumpint rowEndIndex = rowStartIndex + remainder;for (int j = rowStartIndex; j < rowEndIndex; j++) {dump.append(BYTE2HEX[getUnsignedByte(buf, j)]);}dump.append(HEXPADDING[remainder]);dump.append(" |");
​// Ascii dumpfor (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 是非线程安全的

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Leetcode 3186. Maximum Total Damage With Spell Casting

Leetcode 3186. Maximum Total Damage With Spell Casting 1. 解题思路2. 代码实现 题目链接&#xff1a;3186. Maximum Total Damage With Spell Casting 1. 解题思路 这一题就是一个简单的动态规划的题目&#xff0c;我们只需要考虑每一个位置上的元素取或者不取即可&…

决策树概念

图例 概念 决策树基本上就是对经验的总结 决策树的构成&#xff0c;分为两个阶段。构造和剪枝 构造 概念 构造就是生成一颗完整的决策树。构造的过程就是选择什么属性作为节点的过程 构造过程&#xff0c;会存在3种节点 根节点&#xff1a;就是树的最顶端&#xff0c;最…

WPF/C#:异常处理

什么是异常&#xff1f; 在C#中&#xff0c;异常是在程序执行过程中发生的特殊情况&#xff0c;例如尝试除以零、访问不存在的文件、网络连接中断等。这些情况会中断程序的正常流程。 当C#程序中发生这种特殊情况时&#xff0c;会创建一个异常对象并将其抛出。这个异常对象包…

脑机接口,Meta裁50+高管,大模型文本压缩处理,大模型与推荐系统,Luma AI视频工具亮相

更多内容&#xff1a; https://agifun.love 智源社区 北京线下&#xff1a;基于脑机接口的视觉重建前沿进展丨周六直播脑机接口读书会 导语 人类70%以上的感知是通过视觉完成的&#xff0c;且有超过1/3的脑组织参与视觉相关的信息处理&#xff0c;因此视觉是极其重要的感知功…

21.2 文件-列出目录、复制文件、删除文件、配置文件

1. 列出目录 io/ioutil包通过ReadDir函数提供了访问目录的功能。该函数接受一个字符串形式的目录路径参数&#xff0c;返回该目录下按名称字典序排列的文件子目录列表。 entries, err : ioutil.ReadDir("C:\\") entries中都是FileInfo类型变量 文件子目录列表中…

华为云CodeArts API:API管理一体化平台 5月新特性上线啦!

CodeArts API是华为云API全生命周期管理一体化解决方案平台&#xff0c;支持开发者高效实现API设计、API开发、API测试、API托管、API运维、API变现的一站式体验。 通过以API契约为锚点&#xff0c;CodeArts API保证API各阶段数据高度一致&#xff0c;为开发者提供友好易用的A…

FreeRTOS队列(queue)

队列(queue)可以用于"任务到任务"、 "任务到中断"、 "中断到任务"直接传输信息。 1、队列的特性 1、1常规操作 队列的简化操如下图所示&#xff0c;从此图可知&#xff1a; 队列中可以包含若干数据&#xff1a;队列中有若干项&#xff0c;这…

【C++】实现学生管理系统(完整版)

&#x1f495;&#x1f495;&#x1f495;大家好&#xff0c;这是作业侠系列之C实现学生管理系统&#xff0c;还是那句话&#xff0c;大家不想cv或者cv了跑不起来,三连后都可以来找我要源码&#xff0c;私信或评论留下你的邮箱即可。有任何问题有可以私聊我&#xff0c;大家觉得…

YOLOv10涨点改进SPPF创新结构,重新设计全局平均池化层和全局最大池化层,增强全局视角信息和不同尺度大小的特征

本文改进:SPPF_improve利用全局平均池化层和全局最大池化层,加入一些全局背景信息和边缘信息,从而获取全局视角信息并减轻不同尺度大小所带来的影响,强烈推荐,适合直接使用,paper创新级。 目录 1,YOLOv10介绍 1.1 C2fUIB介绍 1.2 PSA介绍 1.3 SCDown 2.SPP &SP…

TSP:人工原生动物优化器(APO)求解旅行商问题TSP(可以更改数据),MATLAB代码

一、旅行商问题介绍 二、人工原生动物优化算法求解TSP 2.1算法介绍 人工原生动物优化器&#xff08;Artificial Protozoa Optimizer &#xff0c;APO&#xff09;由Xiaopeng Wang等人于2024年提出&#xff0c;其灵感来自自然界中的原生动物。APO 模拟了原生动物的觅食、休眠和…

Python合并文件(dat、mdf、mf4)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

Nginx负载均衡之模块

1.服务器配置指令 Nginx 负载均衡是由代理模块和上游&#xff08;upstream&#xff09;模块共同实现的&#xff0c;Nginx 通过代理模块的反向代理功能将用户请求转发到上游服务器组&#xff0c;上游模块通过指定的负载均衡策略及相关的参数配置将用户请求转发到目标服务器上。上…

数据库回表及优化方法(附示例)

数据库回表及优化方法 什么是数据库回表如何优化回表1. 使用覆盖索引示例&#xff1a; 2. 减少查询列示例&#xff1a; 3. 适当增加索引示例&#xff1a; 4. 优化查询语句示例&#xff1a; 示例&#xff1a;优化回表的具体案例原始查询及索引&#xff1a;优化后的索引&#xff…

ThinkPHP 5.1.X 反序列化漏洞:深入分析与利用技巧

在Web应用开发中&#xff0c;框架的安全性至关重要。ThinkPHP作为广泛使用的PHP开发框架&#xff0c;其版本5.1.X中存在一个反序列化漏洞&#xff0c;允许攻击者执行任意代码。本文将深入分析该漏洞的原理、利用方法&#xff0c;并提供相应的防护措施。 漏洞背景 ThinkPHP 5.…

【three.js】设置canvas画布背景透明

通过Three.js渲染一个模型的时候&#xff0c;不希望canvas画布有背景颜色&#xff0c;也就是canvas画布完全透明&#xff0c;可以透过canvas画布看到画布后面叠加的HTML元素图文&#xff0c;呈现出来一种三维模型悬浮在网页上面的效果。 比如我们现在的模型背景是黑色的&#…

spring框架(SSM)

Spring Framework系统架构 Spring框架是一个开源的企业级Java应用程序框架&#xff0c;它为开发Java应用程序提供了一个全方位的解决方案。Spring的核心优势在于它的分层架构&#xff0c;这使得开发者可以灵活选择使用哪些模块而无需引入不需要的依赖。下面是Spring框架的一些关…

【每天学会一个渗透测试工具】AWVS安装及使用指南

&#x1f31d;博客主页&#xff1a;泥菩萨 &#x1f496;专栏&#xff1a;Linux探索之旅 | 网络安全的神秘世界 | 专接本 | 每天学会一个渗透测试工具 ✨AWVS介绍 是应用漏洞扫描工具 &#x1f4a6;使用docker安装 docker pull dockermi3aka/awvs启动镜像 docker run -dit …

数据采集项目1-用户行为数据同步

环境准备 linux配置、克隆103和104、编写集群分发脚本、ssh无密码登录配置、jdk安装、数据模拟集群日志数据输出脚本、xcall脚本、安装hadoop、zk安装、kafka安装、flume安装、mysql安装、maxwell安装、datax安装、hive安装 用户行为数据同步-总的数据流程图 第一层flume 数据…

Go基础编程 - 07 - 字典(map)及其约束

字典&#xff08;map&#xff09; 下一篇&#xff1a;结构体1. 声明2. nil 值字典3. 判断某个键是否存在4. 遍历5. delete() 删除键值对6. 约束7. 扩展 上一篇&#xff1a;指针 下一篇&#xff1a;结构体 map 是一种无序的基于 key-value 的数据结构&#xff0c;Go 语言中的 …

快速使用MetInfo搭建自己的官网

最近一直在找怎么样的开源项目可以用来搭建官网&#xff0c;不仅是个人官网还是企业官网&#xff0c;我发现使用MetInfo搭建官网非常适合&#xff0c;整体的步骤可以归纳为以下几步&#xff0c;我也一并描述清晰和准确&#xff1a; 准备域名和主机&#xff1a; 注册一个属于自…