关于Mina中IoBuffer与Java NIO中ByteBufferde的使用

2022年9月25日11:17:24

项目中大量的使用 NIO 和 Mina,虽然之前一直对这部分比较关注,但是还没有好好的总结一下这方面的内容,本节内容介绍一下NIO 里最基本的一个类 ByteBuffer。

关于Mina中的 IoBuffer,我们可以先看 Mina API 中的描述:

A byte buffer used by MINA applications. This is a replacement for ByteBuffer. Please refer to ByteBuffer documentation for preliminary usage。

当然,接下去也有写到:MINA does not use NIO ByteBuffer directly for two reasons,至于这 Two Reasons,我们将在后面的比较中展开。


1. ByteBuffer

ByteBuffer 继承了 Buffer,对 Buffer 的理解可以说是 NIO 的入门。

在 Buffer 中有 4 个重要的属性:

  • Capacity: the capacity is set when the buffer is created and can never be changed。
  • Limit: the first element of the buffer that should not be read or written
  • Position: the index of the next element to be read or written
  • Mark: a remembered position. Calling mark() set mark=position

它们的关系如下:

0<=mark<=position<=limit<=capacity

通俗的讲:

  • Capacity:开的内存的大小,一旦设定了,就不能更改了。注意,这里指的是原生的 NIO。
  • Limit:可以分读写来统计。在写入 buffer 时,limit 表示有多少空间可以写入。在从 buffer 写出时,limit 表示有多少可以写出。
  • Position:下一个要被读或写的位置。
  • Mark:标记位,可以记住某个 position,方便后续操作。

2. ByteBuffer的常用方法

flip() 读写模式的转换。
rewind() 将 position 重置为 0 ,一般用于重复读。
clear() 清空 buffer ,准备再次被写入 (position 变成 0 , limit 变成 capacity) 。
compact() 将未读取的数据拷贝到 buffer 的头部位。
mark() 可以标记一个位置
reset() 可以重置到该位置。
get()、getShort() 获取 ByteBuffer 中的内容,当然这里 get 的内容都是从 position 开始的,所以要时刻注意 position。每次 get 之后 position 都会改变。Position 的变化是根据你 get 的类型,如果是 short,那就是 2 个 byte,如果是 int,那就是增加 4 个 byte,即 32。
put()、putShort() 向 ByteBuffer 添加内容,这里 put 的内容都是从 position 开始的。每次 put 之后 position 都会改变。

当然还有 allocate、hasRemaining 等常用的方法,不过这些用法一般都不会出错,使用起来和 4 个 attributes 也没有多大相关。

特别注意:Buffers are not thread-safe. If you want to access a given buffer concurrently from multiple threads, you will need to do your own synchronization prior to accessing the buffer. 至于 Buffer 或者 ByteBuffer 有什么用?那太多了,只要涉及到传输、涉及到通信,都可以用到。当然你也可以用它最原始的含义,缓冲。


3. 举个栗子:

package com.hl.magic.mina3;

import ch.qos.logback.core.encoder.ByteArrayUtil;
import org.junit.jupiter.api.Test;

import java.nio.ByteBuffer;

/**
 * @Author HL
 * @Date 2022/7/6 20:49
 */
public class ByteBufferDemo {

	private final byte[] byteValue = new byte[]{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10};

	@Test
	public void byteBufferWrapTest() {
		System.out.println("origin Data is : [" + ByteArrayUtil.toHexString(byteValue) + "]");
		// 将byte数组放在buffer缓冲区
		ByteBuffer buffer = ByteBuffer.wrap(byteValue);

		// 取出头部两个字节的buffer数组
		byte[] result = new byte[2];
		buffer.get(result);
		System.out.println("result is : [" + ByteArrayUtil.toHexString(result) + "]");

		//取出body部分
		byte[] body = new byte[buffer.limit() - 2];
		buffer.get(body);
		buffer.clear();
		System.out.println("body is : [" + ByteArrayUtil.toHexString(body) + "]");

		//另一种取法
		buffer = ByteBuffer.wrap(byteValue);
		body = new byte[buffer.limit() - 4];
		buffer.position(4);
		buffer.get(body);
		buffer.clear();
		System.out.println("body is : [" +ByteArrayUtil.toHexString(body) + "]");

	}

	@Test
	public void bufferAllocateTest() {

		// 设置一个buffer缓冲区间,区间容量为16字节
		ByteBuffer buffer = ByteBuffer.allocate(16);

		// 向缓冲区间添加数据
		buffer.put(new byte[]{0x07, 0x08, 0x09, 0x10});
		buffer.put(new byte[]{0x00, 0x00, 0x00, 0x00});
		buffer.put(new byte[]{0x01, 0x01, 0x01, 0x01});
		buffer.put(new byte[]{0x02, 0x02, 0x02, 0x02});
		buffer.flip();

		//创建一个字节数组,数组长度为buffer区间容量
		byte[] bytes = new byte[buffer.limit()];
		// 取出所有数据
		buffer.get(bytes);
		buffer.clear();
		System.out.println("body is : [" +ByteArrayUtil.toHexString(bytes) + "]");

		//创建一个新数组,长度为buffer容量- 12字节
		byte[] body = new byte[buffer.limit() - 12];
		//从新的偏移位置处计算,也就是偏移12字节的区间后开始取数据
		buffer.position(12);
		buffer.get(body);
		buffer.clear();
		System.out.println("body is : [" + ByteArrayUtil.toHexString(body) + "]");
	}
}

输出:

origin Data is : [01020304050607080910]

#用例1:
result is : [0102]
body is : [0304050607080910]
body is : [050607080910]

#用例2:
body is : [07080910000000000101010102020202]
body is : [02020202]

好了 NIO 的 ByteBuffer 告一段落,接下来先说 IoBuffer 中说不用 ByteBuffer 的 Two Reasons:


4. IoBuffer

1.  It doesn't provide useful getters and putters such as fill, get/putString, and get/putAsciiInt() enough.

2. It is difficult to write variable-length data due to its fixed capacity

  • 对于第一点,增加了更实用的 getString。
  • 对于第二点,IoBuffer 实现了 Auto Expand 和 Auto Shrink。这就意味了,capacity 可以根据传输内容的大小自动变更了。在使用上,我们可以这样写:
IoBuffer buf = IoBuffer.allocate(1024).setAutoExpand(true);

在 IoBuffer 的源码中,大部分都使用了原生的 ByteBuffer 来实现,这部分采用allocator 来实现。

/** The allocator used to create new buffers */
private static IoBufferAllocator allocator = new SimpleBufferAllocator();

在 SimpleBufferAllocator 的其中一段 allocate:

public ByteBuffer allocateNioBuffer(int capacity, boolean direct) {
        ByteBuffer nioBuffer;
        if (direct) {
            nioBuffer = ByteBuffer.allocateDirect(capacity);
        } else {
            nioBuffer = ByteBuffer.allocate(capacity);
        }
        return nioBuffer;
    }

至于其他操作和 ByteBuffer 类似。

贴一段 Reilly - Java NIO 中的一段代码作为结束,后续更多的 Mina 相关会根据开发进度给出介绍:

import java.nio.CharBuffer;
 
public class BufferFillDrain {
private static int index = 0;
private static String[] strings = { "A random string value",
"The product of an infinite number of monkeys",
"Hey hey we're the Monkees" };
 
private static boolean fillBuffer(CharBuffer buffer) {
 
if (index >= strings.length) {
return false;
}
String string = strings[index++];
 
for (int i = 0; i < string.length(); i++) {
buffer.put(string.charAt(i));
}
return true;
}
 
private static void drainBuffer(CharBuffer buffer){
while(buffer.hasRemaining()){
System.out.println(buffer.get());
}
System.err.println("");
}
 
public static void main(String[] args) {
CharBuffer buffer=CharBuffer.allocate(100);
while(fillBuffer(buffer)){
buffer.flip();
drainBuffer(buffer);
buffer.clear();
}
 
}
}
  • 作者:洒家肉山大魔王
  • 原文链接:https://blog.csdn.net/qq_27706119/article/details/125705368
    更新时间:2022年9月25日11:17:24 ,共 4982 字。