Netty(二)- NIO三大组件之Buffer

news2024/12/26 1:20:59

文章目录

  • 一、Buffer 基本介绍
  • 二、Buffer 类及其子类
  • 三、Buffer 的使用
  • 四、关于Buffer 的注意事项和细节
    • 1. put和get的数据类型应该相同
    • 2. 可以将一个普通 Buffer 转成只读 Buffer
    • 3. 可以使用MappedByteBuffer让文件直接在内存中修改
    • 4. 可以通过 Buffer 数组完成读写操作(Scattering 和 Gathering)

一、Buffer 基本介绍

Buffer(缓冲区):缓冲区本质上是一个可以读写数据的内存块,可以理解成是一个容器对象(含数组),该对象提供了一组方法,可以更轻松地使用内存块,缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化情况。Channel 提供从文件、网络读取数据的渠道,但是读取或写入的数据都必须经由 Buffer。
在这里插入图片描述

二、Buffer 类及其子类

(1)在 NIO 中,Buffer 是一个顶层父类,它是一个抽象类。

(2)常用Buffer子类一览(除boolean之外的7个基本类型对应的buffer)

在这里插入图片描述

(3)Buffer 的四个属性:

public abstract class Buffer {
    // Invariants: mark <= position <= limit <= capacity
    private int mark = -1;
    private int position = 0;
    private int limit;
    private int capacity;
}
属性描述
Capacity容量,即可以容纳的最大数据量;在缓冲区创建时被设定并且不能改变
Limit表示缓冲区的当前终点,不能对缓冲区超过极限的位置进行读写操作,且极限是可以修改的
Position位置,下一个要被读或写的元素的索引,每次读写缓冲区数据时都会改变该值,为下次读写作准备
Mark标记

在这里插入图片描述
(4)Buffer类相关方法

public abstract class Buffer {
    //JDK1.4时,引入的api
    public final int capacity( )//返回此缓冲区的容量
    public final int position( )//返回此缓冲区的位置
    public final Buffer position (int newPositio)//设置此缓冲区的位置
    public final int limit( )//返回此缓冲区的限制
    public final Buffer limit (int newLimit)//设置此缓冲区的限制
    public final Buffer mark( )//在此缓冲区的位置设置标记
    public final Buffer reset( )//将此缓冲区的位置重置为以前标记的位置
    public final Buffer clear( )//清除此缓冲区, 即将各个标记恢复到初始状态,但是数据并没有真正擦除, 后面操作会覆盖
    public final Buffer flip( )//反转此缓冲区
    public final Buffer rewind( )//重绕此缓冲区
    public final int remaining( )//返回当前位置与限制之间的元素个数
    public final boolean hasRemaining( )//告知在当前位置和限制之间是否有元素
    public abstract boolean isReadOnly( );//告知此缓冲区是否为只读缓冲区
 
    //JDK1.6时引入的api
    public abstract boolean hasArray();//告知此缓冲区是否具有可访问的底层实现数组
    public abstract Object array();//返回此缓冲区的底层实现数组
    public abstract int arrayOffset();//返回此缓冲区的底层实现数组中第一个缓冲区元素的偏移量
    public abstract boolean isDirect();//告知此缓冲区是否为直接缓冲区
}

(5)ByteBuffer

从前面可以看出对于 Java 中的基本数据类型(boolean除外),都有一个 Buffer 类型与之相对应,最常用的自然是ByteBuffer 类(二进制数据),该类的主要方法如下:

public abstract class ByteBuffer {
    //缓冲区创建相关api
    public static ByteBuffer allocateDirect(int capacity)//创建直接缓冲区
    public static ByteBuffer allocate(int capacity)//设置缓冲区的初始容量
    public static ByteBuffer wrap(byte[] array)//把一个数组放到缓冲区中使用
    //构造初始化位置offset和上界length的缓冲区
    public static ByteBuffer wrap(byte[] array,int offset, int length)
     //缓存区存取相关API
    public abstract byte get( );//从当前位置position上get,get之后,position会自动+1
    public abstract byte get (int index);//从绝对位置get
    public abstract ByteBuffer put (byte b);//从当前位置上添加,put之后,position会自动+1
    public abstract ByteBuffer put (int index, byte b);//从绝对位置上put
 }

三、Buffer 的使用

public class BasicBuffer {

    public static void main(String[] args) {
        // 创建一个Buffer, 大小为 3, 即可以存放3个int
        IntBuffer intBuffer = IntBuffer.allocate(3);

        // 向buffer 存放数据
        for(int i = 0; i < intBuffer.capacity(); i++) {
            intBuffer.put( i * 2);
        }

        // 将buffer转换,之前是写,现在转换为读
        intBuffer.flip();

        while (intBuffer.hasRemaining()) {
            System.out.println(intBuffer.get());
        }
    }
    
}

输出:

0
2
4

debug查看属性值变化:

在这里插入图片描述

在这里插入图片描述

执行完flip()方法后,position值的变化:

flip()方法源码:

public Buffer flip() {
  limit = position;
  position = 0;
  mark = -1;
  return this;
}

在这里插入图片描述

四、关于Buffer 的注意事项和细节

1. put和get的数据类型应该相同

ByteBuffer 支持类型化的 put 和 get,put 放入的是什么数据类型,get 就应该使用相应的数据类型来取出,否则可能有 BufferUnderflowException 异常;

public class NIOByteBufferPutGet {
    public static void main(String[] args) {
        // 创建一个Buffer
        ByteBuffer buffer = ByteBuffer.allocate(64);

        // 类型化方式放入数据
        buffer.putInt(100);
        buffer.putLong(9);
        buffer.putChar('你');
        buffer.putShort((short) 4);

        // 取出
        buffer.flip();

        System.out.println(buffer.getInt());
        System.out.println(buffer.getLong());
        System.out.println(buffer.getChar());
        System.out.println(buffer.getShort());
    }
}

输出:

100
9

4

2. 可以将一个普通 Buffer 转成只读 Buffer

public class ReadOnlyBuffer {
    public static void main(String[] args) {

        // 创建一个buffer
        ByteBuffer buffer = ByteBuffer.allocate(3);

        for(int i = 0; i < 3; i++) {
            buffer.put((byte)i);
        }

        // 转换成读模式
        buffer.flip();

        // 得到一个只读的Buffer
        ByteBuffer readOnlyBuffer = buffer.asReadOnlyBuffer();
        System.out.println(readOnlyBuffer.getClass());

        // 读取
        while (readOnlyBuffer.hasRemaining()) {
            System.out.println(readOnlyBuffer.get());
        }

        // 抛出ReadOnlyBufferException异常
        readOnlyBuffer.put((byte)100);
    }
}

输出:

class java.nio.HeapByteBufferR
0
1
2
Exception in thread "main" java.nio.ReadOnlyBufferException
	at java.nio.HeapByteBufferR.put(HeapByteBufferR.java:172)
	at com.lwk.nettydemo.nio.ReadOnlyBuffer.main(ReadOnlyBuffer.java:28)

3. 可以使用MappedByteBuffer让文件直接在内存中修改

NIO 还提供了 MappedByteBuffer,可以让文件直接在内存(堆外的内存)中进行修改,不用进行拷贝,提高了性能,而如何同步到文件由 NIO 来完成

/**
 * MappedByteBuffer 可让文件直接在内存(堆外内存)修改,操作系统不需要再拷贝一次
 */
public class MappedByteBufferTest {
    public static void main(String[] args) throws Exception {

        RandomAccessFile randomAccessFile = new RandomAccessFile("1.txt", "rw");
        // 获取对应的通道
        FileChannel channel = randomAccessFile.getChannel();

        /**
         * 参数1:FileChannel.MapMode.READ_WRITE 使用的读写模式
         * 参数2:0:可以直接修改的起始位置
         * 参数3:5:是映射到内存的大小(不是索引位置),即 将1.txt的5个字节映射到内存
         * 可以直接修改的范围就是 0-5
         * 实际类型 DirectByteBuffer
         */
        MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, 5);

        mappedByteBuffer.put(0, (byte) 'H');
        mappedByteBuffer.put(3, (byte) '9');
        //IndexOutOfBoundsException异常,因为索引0-4已经占了5个字节,所以再修改索引5时,对应的字节超过了范围,报错
        //mappedByteBuffer.put(5, (byte) 'Y');

        randomAccessFile.close();
        System.out.println("修改成功~~");

    }
}

注意:在程序目录下发现没有修改成功
在这里插入图片描述

但其实在磁盘目录下已经修改成功
在这里插入图片描述

4. 可以通过 Buffer 数组完成读写操作(Scattering 和 Gathering)

前面讲的读写操作,都是通过一个Buffer 完成的,NIO 还支持通过多个Buffer (即 Buffer 数组) 完成读写操作,即 Scattering 和 Gathering

/**
 * Scattering:将数据写入到buffer时,可以采用buffer数组,依次写入  【分散】
 * Gathering: 从buffer读取数据时,可以采用buffer数组,依次读 【聚集】
 */
public class ScatteringAndGatheringTest {
    public static void main(String[] args) throws Exception {

        // 使用 ServerSocketChannel 和 SocketChannel 网络
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        InetSocketAddress inetSocketAddress = new InetSocketAddress(7000);

        // 绑定端口到socket ,并启动
        serverSocketChannel.socket().bind(inetSocketAddress);

        // 创建buffer数组
        ByteBuffer[] byteBuffers = new ByteBuffer[2];
        byteBuffers[0] = ByteBuffer.allocate(5);
        byteBuffers[1] = ByteBuffer.allocate(3);

        // 等客户端连接(telnet)
        SocketChannel socketChannel = serverSocketChannel.accept();
        // 假定从客户端接收8个字节
        int messageLength = 8;
        // 循环读取
        while (true) {

            int byteRead = 0;

            while (byteRead < messageLength) {
                long l = socketChannel.read(byteBuffers);
                // 累计读取的字节数
                byteRead += l;
                System.out.println("byteRead=" + byteRead);
                // 使用流打印,看看当前的这个buffer的 position 和 limit
                Arrays.stream(byteBuffers).map(buffer -> "postion=" + buffer.position() + ", limit=" + buffer.limit()).forEach(System.out::println);
            }

            // 将所有的buffer进行flip
            Arrays.asList(byteBuffers).forEach(ByteBuffer::flip);

            // 将数据读出显示到客户端
            long byteWirte = 0;
            while (byteWirte < messageLength) {
                long l = socketChannel.write(byteBuffers); //
                byteWirte += l;
            }

            // 将所有的buffer进行clear
            Arrays.asList(byteBuffers).forEach(ByteBuffer::clear);

            System.out.println("byteRead:=" + byteRead + " byteWrite=" + byteWirte + ", messagelength" + messageLength);
        }

    }
}

当发送6个字节时:

在这里插入图片描述

当发送8个字节时:

在这里插入图片描述

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

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

相关文章

富文本编辑器 ck-editor5 的使用

最近在项目中需要用到富文本编辑器&#xff0c;据说ck-editor5很不错&#xff0c;于是就使用它了&#xff0c;不过在期间也遇到了很多问题&#xff0c;这里记录下。 一、引入ck-editor5 文档地址&#xff1a;Predefined builds - CKEditor 5 Documentation 这里有个坑&#…

YOLOv2-yolo9000-batter,faster,stronger 论文精度

参考大佬&#xff1a;同济子豪兄 YOLOv2-yolo9000 yolo9000-batter,faster,stronger Introduction 我们提出了一种新的方法来利用大量的图像分类数据&#xff0c;来扩大当前检测系统的范围。我们的方法使用目标分类的分层视图&#xff0c;允许我们将不同的数据集组合在一起…

个人需求1:代码提交这块的做法

老早之前的代码提交的步骤&#xff0c;真的是很繁琐在这里提交代码&#xff0c;现在和大家分析一下我当初的心得。 1.首先黄老师建立了一个jira号,记得把功能背景/方案说明/测试范围提前了解,问起来的时候也能回答,如下图1: 15324就是我本次的jira号,每次开发前先建立jira号,这…

小区访客导航GIS方案

1 应用功能设计 1.1 小区地图 1.1.1 小区地图浏览 基于GIS平台&#xff0c;对小区地图进行渲染发布&#xff0c;提供可视化显示浏览地图可以选择2D地图模式&#xff0c;用户可以在地图上快速查询和点击选择某个小区内的建筑物、POI等信息&#xff0c;并查看其相关属性信息&…

【现代机器人学】学习笔记二:刚体运动

这一节内容最开始在学之前是有些不屑的&#xff0c;这些坐标变换的内容天天都在玩&#xff0c;有什么复杂的&#xff1f;高翔博士的14讲貌似讲这些内容只用了几页。 不过认真一读才发现自己自大肤浅了。 之前我在北大研究院的时候&#xff0c;有一个实验室双聘的浙农林的老师&…

每天一个面试题:ThreadLocal底层原理和实现Demo

ThreadLocal底层原理和实现Demo每天一个面试题&#xff1a;ThreadLocal实现ThreadLocal的DemoThreadLocal底层原理为什么ThreadLocalMap的key设计为弱引用今天开始一个新专栏&#xff1a;每天一个面试题系列 也没有拿到令人心动的offer&#xff0c;看来自己学习方式和能力还是差…

fastapi_No.25_获取配置项

文章目录方式1&#xff1a;隐藏在环境变量中配置环境变量Windows中配置环境变量Linux中配置环境变量在代码中获取环境变量方式2&#xff1a;隐藏在配置文件中装包在代码中获取配置文件内容在之前的代码中&#xff0c;像数据库连接信息等敏感信息&#xff0c;都直接写在代码中&a…

每日一题 —— 882. 细分图中的可到达节点

882. 细分图中的可到达节点 给你一个无向图&#xff08;原始图&#xff09;&#xff0c;图中有 n 个节点&#xff0c;编号从 0 到 n - 1 。你决定将图中的每条边 细分 为一条节点链&#xff0c;每条边之间的新节点数各不相同。 图用由边组成的二维数组 edgesedgesedges 表示&…

182:vue+openlayers 使用d3实现地图区块呈现不同颜色的效果

第182个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+openlayers中加载解析geojson文件,同时利用d3的颜色功能,使得美国每个州呈现出不同的颜色区块,方便识别。 直接复制下面的 vue+openlayers源代码,操作2分钟即可运行实现效果; 注意如果OpenStreetMap无法加载,…

UNIX环境高级编程_文件IO_文件描述符

这篇文章记录文件描述符&#xff0c;下一篇文章记录文件描述表。 1 文件描述符 先说说什么是文件IO。文件的IO就是文件的输入输出&#xff0c;也就是文件的读写。读和写是以CPU为参考的&#xff0c;从CPU向文件中写入数据&#xff0c;就是写操作&#xff1b;从文件中读取数据…

Aviation turbofan starting model

Aviation turbofan starting model 涡扇发动机(Turbofan)即涡轮风扇发动机,来源于涡轮喷气发动机,主要是为了解决涡轮喷气发动机耗油率过高的问题。其结构特点是流过风扇的空气一部分进入压气机(内涵道),一部分进入由压气机外部通道(外涵道)流过,这部分气流不经过燃烧…

01 - Linux系统概要(再论计算机系统)

---- 整理自狄泰软件唐佐林老师课程 1. 再论计算机系统 计算机系统由躯体和灵魂两部分组成 – 躯体&#xff1a;构成计算机系统的电子设备&#xff08;硬件&#xff09; – 灵魂&#xff1a;指挥躯体完成动作的指令序列&#xff08;软件&#xff09; 躯体核心&#xff1a;中央…

2022-12-05 优化el-tree懒加载选人树

今后就都拼抵抗力了嗷 需求描述 此处有一棵懒加载树&#xff08;可选人&#xff09;&#xff0c;右侧展示已选中的人。且父子关联&#xff0c;可以通过选中一个部门勾选所有子节点。问题是&#xff0c;选中父节点&#xff0c;当子节点未加载时&#xff0c;是获取不到勾选的子…

Redis配置、持久化以及相命令

Redis 什么是Redis Redis&#xff08;远程字典服务器&#xff09;是一个开源的、使用C语言编写的NoSQL数据库 Redis 基于内存运行并支持持久化&#xff0c;采用key-value&#xff08;键值对&#xff09;的存储形式&#xff0c;是目前分布式架构中不可或缺的一环。 Redis服务…

新来的性能测试工程师工资25K,看了他做的性能测试,那才真叫牛

一直深耕于互联网行业的测试工作&#xff0c;前期测试主要以项目为主&#xff0c;也就是 一个人负责2-3个项目 的测试工作&#xff0c;当然包括项目上功能、自动化和性能等一切测试工作。 我有几个朋友也在互联网大厂工作&#xff0c;从他们当中了解到其实真正的互联网大厂&…

【前端CSS】网站都变成灰色了,它是怎么实现的?(含源代码解析)

目录&#xff1a;网站都变成灰色了&#xff0c;它是怎么实现的&#xff1f;一、前言二、如何实现的三、代码的理解3.1 CSS3 filter(滤镜) 属性3.2 定义和使用3.2.1 CSS动画演示3.2.2 JS语法演示3.3 浏览器支持3.4 CSS 语法3.5 Filter 函数四、实例展示4.1 模糊实例4.2 Brightne…

3、JSP——Servlet、IDEA创建Web项目、IDEA创建JSP页面

目录 一、Servlet的概念 二、Servlet的作用 三、IDEA中创建Web项目 四、手动部署 五、自动部署 1、IDEA部署Tomcat服务器 2、IDEA部署JavaWeb项目 3、JSP页面 一、Servlet的概念 &#xff08;1&#xff09;Servlet&#xff1a;Server Applet的简称&#xff0c;是运…

云服务器配置Code-Server环境并运行Python和C++

目录1、前言2、部署流程2.1 前置准备2.2 运行docker安装Code-Server3、运行Code-Server3.1 配置运行环境3.2 运行Python3.3 运行C1、前言 云服务器需要配置C开发环境&#xff0c;了解到有code-server这个VsCode提供的云端服务&#xff0c;因此选择在云服务器上部署。 2、部署流…

D3--FPGA IIC接口通信2022-12-05

1.IIC简介 1.1 IIC概述 IIC即 Inter-Integrated Circuit(集成电路总线&#xff09;&#xff0c;是由 Philips 半导体公司在八十年代初设计出来的一种简单、双向、二线制总线标准。多用于主机和从机在数据量不大且传输距离短的场合下的主从通信。主机启动总线&#xff0c;并产…

[附源码]计算机毕业设计少儿节目智能推荐系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…