【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模板网!

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

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

相关文章

  • 理解缓冲区

    对于这样的代码,首先可以肯定的是 printf 语句先于 sleep 执行,既然如此那么就应该是先打印语句然后进行休眠,下面看看结果: 但这里却是先休眠以后再打印语句,这是因为存在一个叫缓冲区的东西,当我们要向外设写入数据(让显示器显示就是向显示器写入数据)时会将

    2023年04月25日
    浏览(71)
  • 【Linux】理解缓冲区

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

    2024年01月23日
    浏览(52)
  • C/C++缓冲区

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

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

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

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

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

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

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

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

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

    2024年02月13日
    浏览(47)
  • 漏洞利用与缓冲区溢出攻击

    目录 简介: 1. 漏洞利用基础 2. 缓冲区溢出攻击 3. 缓解缓冲区溢出攻击 3.1 边界检查 3.2 使用安全函数 3.3 使用堆栈保护技术 总结: 简介: 漏洞利用是渗透测试中的重要部分,它允许攻击者通过利用软件或系统的漏洞来获取未经授权的访问权限。其中,缓冲区溢出攻击是最常

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

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

    2024年02月03日
    浏览(48)
  • 浅谈linux缓冲区的认识!

    今天来为大家分享一波关于缓冲区的知识!那么既然我们要谈缓冲区,那么就得从是什么?为什么?有什么作用这几个方面来谈论一下缓冲区!然后再通过一些代码来更加深刻的理解缓冲区的知识! 从最简单的理解方面来,我们可以将缓冲区理解成一块内存!那么这块内存是

    2024年02月05日
    浏览(54)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包