【Netty】字节缓冲区 ByteBuf(七)(下)

这篇具有很好参考价值的文章主要介绍了【Netty】字节缓冲区 ByteBuf(七)(下)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

回顾Netty系列文章:

  • Netty 概述(一)
  • Netty 架构设计(二)
  • Netty Channel 概述(三)
  • Netty ChannelHandler(四)
  • ChannelPipeline源码分析(五)
  • 字节缓冲区 ByteBuf (六)(上)

在了解了 ByteBuffer 的原理之后,再来理解Netty 的 ByteBuf 就比较简单了。

ByteBuf 是 Netty 框架封装的数据缓冲区,区别于 position、limit、flip等属性和操作来控制 ByteBuffer 的读写,ByteBuf 通过两个位置指针来协助缓冲区的读写操作,分别是readIndex和writeIndex。

readIndex、writeIndex和capacity变量存在以下关系:

0 <= readIndex <= writeIndex <= capacity

一、实现原理

初始化 ByteBuffer 时,readIndex 和 writeIndex 取值一开始都是0。如下图所示:
【Netty】字节缓冲区 ByteBuf(七)(下)

当执行写入数据之后,writeIndex会增加,如下图所示:
【Netty】字节缓冲区 ByteBuf(七)(下)

当执行读入数据之后则会使readIndex增加,但不会超过writeIndex,如下图:
【Netty】字节缓冲区 ByteBuf(七)(下)

在读取之后,索引 0 到 readIndex位置的区域被视为废弃字节(discard)。可以调用discardReadBytes方法,来释放这部分空间,其作用类似于 ByteBuffer的compact()方法,移除无用的数据,实现缓冲区的重复利用。如下图,展示了执行discardReadBytes后的情况,相当于可写的空间变大了。
【Netty】字节缓冲区 ByteBuf(七)(下)

二、ByteBuf 的使用案例

为了更好的理解ByteBuf,编写了以下示例:

public class ByteBufDemo {


	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// 创建一个缓冲区
		ByteBuf buffer = Unpooled.buffer(10);
		System.out.println("------------初始时缓冲区------------");
		printBuffer(buffer);

		// 添加一些数据到缓冲区中
		System.out.println("------------添加数据到缓冲区------------");

		String s = "love";
		buffer.writeBytes(s.getBytes());
		printBuffer(buffer);

		// 读取数据
		System.out.println("------------读取数据------------");

		while (buffer.isReadable()) {
			System.out.println(buffer.readByte());
		}

		printBuffer(buffer);

		// 执行compact
		System.out.println("------------执行discardReadBytes------------");
		buffer.discardReadBytes();
		printBuffer(buffer);

		// 执行clear
		System.out.println("------------执行clear清空缓冲区------------");
		buffer.clear();
		printBuffer(buffer);

	}

	/**
	 * 打印出ByteBuf的信息
	 * 
	 * @param buffer
	 */
	private static void printBuffer(ByteBuf buffer) {
		System.out.println("readerIndex:" + buffer.readerIndex());
		System.out.println("writerIndex:" + buffer.writerIndex());
		System.out.println("capacity:" + buffer.capacity());
	}
}

输出结果:

-----------初始时缓冲区------------
readerIndex:0
writerIndex:0
capacity:10
------------添加数据到缓冲区------------
readerIndex:0
writerIndex:4
capacity:10
------------读取数据------------
108
111
118
101
readerIndex:4
writerIndex:4
capacity:10
------------执行discardReadBytes------------
readerIndex:0
writerIndex:0
capacity:10
------------执行clear清空缓冲区------------
readerIndex:0
writerIndex:0
capacity:10

Process finished with exit code 0

对比ByteBuffer和ByteBuf两个示例可以看出,Netty 提供了更加方便地创建ByteBuf的工具(unpooled),同时,也不必再执行flip()方法来切换读写模式。对比而言,ByteBuf更加易于使用。

三、ByteBuf 的3种使用模式

ByteBuf 共有三种使用模式:堆缓冲区模式(Heap Buffer)、直接缓冲区模式(Direct Buffer)和 复合缓冲区模式(Composite Buffer)。

3.1 堆缓冲模式

堆缓冲区模式又称为支撑数组,其数据是存放在JVM的堆空间,通过将数据存储在数组中实现。

优点:数据存储在JVM堆中可以快速的创建和快速释放,并且提供了数据快速访问的方法;
缺点:每次数据与 I/O 进行传输时,都需要将数据复制到直接缓冲区。

以下是堆缓冲区的代码示例:

public class ByteBufHeapBufferDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		// 创建一个堆缓冲区
		ByteBuf buffer = Unpooled.buffer(10);
		String s = "waylau";
		buffer.writeBytes(s.getBytes());

		// 检查是否是支撑数组
		if (buffer.hasArray()) {

			// 获取支撑数组的引用
			byte[] array = buffer.array();

			// 计算第一个字节的偏移量
			int offset = buffer.readerIndex() + buffer.arrayOffset();

			// 可读字节数
			int length = buffer.readableBytes();
			printBuffer(array, offset, length);
		}
	}

	/**
	 * 打印出Buffer的信息
	 * 
	 * @param buffer
	 */
	private static void printBuffer(byte[] array, int offset, int len) {
		System.out.println("array:" + array);
		System.out.println("array->String:" + new String(array));
		System.out.println("offset:" + offset);
		System.out.println("len:" + len);
	}
}

输出结果:

array:[B@5b37e0d2
array->String:waylau    
offset:0
len:6

Process finished with exit code 0

3.2 直接缓冲区模式

直接缓冲区属于堆外分配的直接内存,不会占用堆得空间。

优点:使用 socket 传输数据时性能很好,避免了数据从 JVM 堆内存复制到直接缓冲区的过程,提高了性能。
缺点:相对于堆缓冲区而言,直接缓冲区分配内存空间和释放更为昂贵。
对于涉及大量的 I/O 数据的读写,建议使用直接缓冲区。而对于用于后端业务消息编解码模块,建议使用堆缓冲区。
以下是直接缓冲区代码示例:

public class ByteBufDirectBufferDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		// 创建一个直接缓冲区
		ByteBuf buffer = Unpooled.directBuffer(10);
		String s = "waylau";
		buffer.writeBytes(s.getBytes());

		// 检查是否是支撑数组.
		// 不是支撑数组,则为直接缓冲区
		if (!buffer.hasArray()) {

			// 计算第一个字节的偏移量
			int offset = buffer.readerIndex();

			// 可读字节数
			int length = buffer.readableBytes();

			// 获取字节内容
			byte[] array = new byte[length];
			buffer.getBytes(offset, array);

			printBuffer(array, offset, length);
		}
	}

	/**
	 * 打印出Buffer的信息
	 * 
	 * @param buffer
	 */
	private static void printBuffer(byte[] array, int offset, int len) {
		System.out.println("array:" + array);
		System.out.println("array->String:" + new String(array));
		System.out.println("offset:" + offset);
		System.out.println("len:" + len);
	}
}

输出结果:

java复制代码array:[B@6d5380c2
array->String:waylau
offset:0
len:6

Process finished with exit code 0

3.3 复合缓冲区模式

复合缓冲区是 Netty 特有的缓冲区。本质上类似于提供一个或多个 ByteBuf 的组合视图,可以根据需要添加和删除不同类型的 ByteBuf。

优点:提供了一种访问方式让使用者自由地组合多个ByteBuf,避免了复制和分配新的缓冲区。
缺点:不支持访问其支撑数组。因此如果要访问,需要先将内容复制到堆内存中,再进行访问。
以下示例是复合缓冲区将堆缓冲区和直接缓冲区组合在一起,没有进行任何复制过程,仅仅创建了一个视图而已。

public class ByteBufCompositeBufferDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		// 创建一个堆缓冲区
		ByteBuf heapBuf = Unpooled.buffer(3);
		String way = "way";
		heapBuf.writeBytes(way.getBytes());

		// 创建一个直接缓冲区
		ByteBuf directBuf = Unpooled.directBuffer(3);
		String lau = "lau";
		directBuf.writeBytes(lau.getBytes());

		// 创建一个复合缓冲区
		CompositeByteBuf compositeBuffer = Unpooled.compositeBuffer(10);
		compositeBuffer.addComponents(heapBuf, directBuf); // 将缓冲区添加到符合缓冲区

		// 检查是否是支撑数组.
		// 不是支撑数组,则为复合缓冲区
		if (!compositeBuffer.hasArray()) {

			for (ByteBuf buffer : compositeBuffer) {
				// 计算第一个字节的偏移量
				int offset = buffer.readerIndex();

				// 可读字节数
				int length = buffer.readableBytes();

				// 获取字节内容
				byte[] array = new byte[length];
				buffer.getBytes(offset, array);

				printBuffer(array, offset, length);
			}

		}
	}

	/**
	 * 打印出Buffer的信息
	 * 
	 * @param buffer
	 */
	private static void printBuffer(byte[] array, int offset, int len) {
		System.out.println("array:" + array);
		System.out.println("array->String:" + new String(array));
		System.out.println("offset:" + offset);
		System.out.println("len:" + len);
	}
}

输出结果:

array:[B@4d76f3f8
array->String:way
offset:0
len:3
array:[B@2d8e6db6
array->String:lau
offset:0
len:3

Process finished with exit code 0

CompositeByteBuf是一个虚拟的缓冲区,其用途是将多个缓冲区显示为单个合并缓冲区,类似数据库中的视图。

总结

通过以上对于ByteBuf的介绍,相信小伙伴们对于ByteBuf的原理也有了一定的了解。文章来源地址https://www.toymoban.com/news/detail-459034.html

到了这里,关于【Netty】字节缓冲区 ByteBuf(七)(下)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Linux】理解缓冲区

    我们发现 printf 和 fwrite (库函数)都输出了2次,而 write 只输出了一次(系统调用)。为什么呢?肯定和fork有关! C接口的函数被打印了两次系统接口前后只是打印了一次:和fork函数有关,fork会创建子进程。在创建子进程的时候,数据会被处理成两份,父子进程发生写时拷

    2024年01月23日
    浏览(54)
  • 【Linux】文件缓冲区

    提到文件缓冲区这个概念我们好像并不陌生,但是我们对于这个概念好像又是模糊的存在脑海中,之间我们在介绍c语言文件操作已经简单的提过这个概念,今天我们不妨深入理解什么是文件缓冲区 通过自己实现库中的一些文件操作函数更加深入的理解文件缓冲区 自定义实现

    2024年02月10日
    浏览(58)
  • C/C++缓冲区

    什么是缓冲区? 程序和磁盘文件之间不能直接交换数据,必须通过内存中一个被称为文件缓冲区的区域来中转。ANSIC标准规定,系统会自动为每个正在使用的文件在内存中开辟一个缓冲区,缓冲区的大小随机器而异。 缓冲区有什么作用? 假设我们在家中休息看电视吃零食,

    2024年02月15日
    浏览(50)
  • SEED-缓冲区溢出攻击

    实验环境:SEED-Ubuntu20.04虚拟机 a) 缓冲区溢出原理 **缓冲区溢出攻击原理:**利用溢出的数据改变源程序的控制流,如覆盖返回地址 b) 分析生成badfile文件的exploit.py程序 Shellcode部分 字节数组末尾处填入shellcode c) 编译目标服务器上具有缓冲区溢出漏洞的stack.c程序,并将其缓冲

    2024年02月07日
    浏览(45)
  • 【Linux】深入理解缓冲区

    目录 什么是缓冲区 为什么要有缓冲区 缓冲区刷新策略 缓冲区在哪里  手动设计一个用户层缓冲区 缓冲区本质上一块内存区域,用来保存临时数据。 缓冲区在各种计算任务中都广泛应用,包括输入/输出操作、网络通信、图像处理、音频处理等。 这块内存区域是由 谁提供的

    2024年02月15日
    浏览(64)
  • 【linux】重定向+缓冲区

    自我名言 : 只有努力,才能追逐梦想,只有努力,才不会欺骗自己。 喜欢的点赞,收藏,关注一下把! close(1),为什么没有打印新建文件fd呢? printf(“%dn”,fd); printf会把内容打印到stdout文件中。 但是close(1)关闭标准输出stdout—显示器,int fd=open();新打开的文件fd是1。 st

    2024年02月08日
    浏览(56)
  • 网络安全——缓冲区溢出攻击

    1、缓冲区溢出概述 什么是缓冲区?它是指程序运行期间,在内存中分配的一个连续的区域,用于保存包括字符数组在内的各种数据类型。所谓溢出,其实就是所填充的数据超出了原有的缓冲区边界,并非法占据了另一段内存区域。 两者结合进来,所谓缓冲区溢出,就是由于

    2024年02月13日
    浏览(48)
  • TCP缓冲区参数调优

    1、系统原值查询 2、设置值 3、参数解析 tcp_wmem(3个INTEGER变量): min, default, max **min:**为TCP socket预留用于发送缓冲的内存最小值。每个tcp socket都可以在建议以后都可以使用它。默认值为4096(4K)。 **default:**为TCP socket预留用于发送缓冲的内存数量,默认情况下该值会影响其它

    2024年02月11日
    浏览(42)
  • [操作系统安全]缓冲区溢出

    一、C栈帧结构 函数调用内存中的三个区域,代码区、静态数据区、动态数据区(压栈和清栈就是在这个区域完成的)。 CPU 中有三个寄存器,分别是eip、ebp和esp。eip永远指向代码区中将要执行的下一条指令,执行方式包括顺序执行和跳转;ebp和esp用于管理栈空间,ebp指向栈底

    2024年02月03日
    浏览(41)
  • Linux之缓冲区的理解

    目录 一、问题引入 二、缓冲区 1、什么是缓冲区 2、刷新策略 3、缓冲区由谁提供 4、重看问题 三、缓冲区的简单实现 我们先来看看下面的代码:我们使用了C语言接口和系统调用接口来进行文件操作。在代码的最后,我们还使用fork函数创建了一个子进程。  代码运行结果如

    2024年02月03日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包