Java循环ByteBuffer实现

Java循环ByteBuffer实现

[TOC]

网络分包

应用程序需要多次从网络读取数据,每次读取的数据长度不固定,每次读取的数据也不能保证是一个完整的业务报文,那么如何做到读取完整的业务报文呢?这就是网络分包问题。在BIO时代,因为使用的是阻塞式读取,可以读够指定长度的报文再返回。但在NIO、AIO等非阻塞时代,则没办法读取指定的长度。 以常见的NIO Reactor模式为例子,Selector每次select返回后,如果是readable,那么读取的数据可以只读一次,也可以循环读取(只要SocketChannel.read方法返回大于0的数据,并且对应的ByteBuffer还有可用空间): 循环读取示例

ByteBuffer buf = ByteBuffer.allocate(512);
int ret = 0;
int readBytes = 0;
while ((ret = sc.read(buf)) > 0) {
	readBytes += ret;
	if (!buf.hasRemaining()) { // 如果缓冲区读满,break
		break;
	}
}

但是需要将每次readable读取返回的ByteBuffer存起来,将里面的数据拼在一块,再按照业务规则分包。可以用一个Queue<ByteBuffer>;也可以用一个大的ByteBuffer,将读取到的ByteBuffer写入大的也可以用一个大的ByteBuffer。这两种方式都会导致过多的ByteBuffer操作,显得不是那么好。 还有一种方式就是使用循环ByteBuffer。

什么是循环ByteBuffer

循环ByteBuffer可以简单地理解成是一个byte[]数组,长度为capacity,首尾相连,构成一个环形,按照操作数组的方向写数据和读数据,可以循环使用。 循环ByteBuffer维护写索引writeIndex和读索引readIndex,分别表示下一个可以写、可以读的byte的数组下标。同时维护一个状态empty,表示是否为空。既然是做为缓存使用,那么当空间不足的时候自动扩容是必要的功能,这里实现的循环ByteBuffer每次扩容为原来的2倍大小。

实现难点

  • 存入数据的时候(storeData),有可能数据是连续存储的,也有可能是一部分存在数组尾部,一部分存在数组头部;
  • 读取数据的时候(fetchData),也是一样的,可能是连接的,也可能在尾部读一部分,再在头部读一部分;
  • 得到空闲空间大小和可读取数据大小时,也需要根据writeIndex和readIndex的大小关系采取不同的处理方式;
  • 需要支持预读取数据,即读取过后,不移动readIndex,以便下次仍然从同样位置读取;

代码实现

org.alive.learn.heartbeat.CircleByteBuffer

package org.alive.learn.heartbeat;

import java.util.Arrays;

/**
 * <p>
 * Cicle Buffer类,底层采用byte[]实现的循环Buffer,支持存取数据,空间不够时长度为自动变为原来的两倍;
 * 
 * <ul>
 * <li>用途:可做为Nio读取数据的缓存,实现分包处理;
 * <li>本类不是线程安全的;
 * <li>为简单起见,未采用DirectMemory,使用的JVM Heap内存;
 * <li>因为底层采用的数组存储数据,所以数据有可能不连续;如果按照首尾相联的环形来理解,则是连续的;
 * </ul>
 * 
 * @author myumen
 * @since 2017.06.28
 */
public class CircleByteBuffer {
	/** 默认大小为16字节 */
	private final stat
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值