`
chenpi529
  • 浏览: 6439 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

bytebuffer 转载 http://www.blogjava.net/Green-nut/articles/332202.html

    博客分类:
  • java
阅读更多
在NIO中,数据的读写操作始终是与缓冲区相关联的.读取时信道(SocketChannel)将数据读入缓冲区,写入时首先要将发送的数据按顺序填入缓冲区.缓冲区是定长的,基本上它只是一个列表,它的所有元素都是基本数据类型.ByteBuffer是最常用的缓冲区,它提供了读写其他数据类型的方法,且信道的读写方法只接收ByteBuffer.因此ByteBuffer的用法是有必要牢固掌握的.

1.创建ByteBuffer
1.1 使用allocate()静态方法
    ByteBuffer buffer=ByteBuffer.allocate(256);
    以上方法将创建一个容量为256字节的ByteBuffer,如果发现创建的缓冲区容量太小,唯一的选择就是重新创建一个大小合适的缓冲区.

1.2 通过包装一个已有的数组来创建
    如下,通过包装的方法创建的缓冲区保留了被包装数组内保存的数据.
    ByteBuffer buffer=ByteBuffer.wrap(byteArray);

    如果要将一个字符串存入ByteBuffer,可以如下操作:
    String sendString="你好,服务器. ";
    ByteBuffer sendBuffer=ByteBuffer.wrap(sendString.getBytes("UTF-16"));

2.回绕缓冲区
  buffer.flip();
  这个方法用来将缓冲区准备为数据传出状态,执行以上方法后,输出通道会从数据的开头而不是末尾开始.回绕保持缓冲区中的数据不变,只是准备写入而不是读取.

3.清除缓冲区
  buffer.clear();
  这个方法实际上也不会改变缓冲区的数据,而只是简单的重置了缓冲区的主要索引值.不必为了每次读写都创建新的缓冲区,那样做会降低性能.相反,要重用现在的缓冲区,在再次读取之前要清除缓冲区.

4.从套接字通道(信道)读取数据
  int bytesReaded=socketChannel.read(buffer);
  执行以上方法后,通道会从socket读取的数据填充此缓冲区,它返回成功读取并存储在缓冲区的字节数.在默认情况下,这至少会读取一个字节,或者返回-1指示数据结束.

5.向套接字通道(信道)写入数据
  socketChannel.write(buffer);
  此方法以一个ByteBuffer为参数,试图将该缓冲区中剩余的字节写入信道.
发表于 2010-09-16 16:47 fanxu 阅读(5032) 评论(5)  编辑  收藏

评论
# 字节缓冲区
ByteBuffer其实就是一个字节缓冲区, 在这里你可以对缓冲区的数据进行 字节级的操作. 这样的好处在于你可以比较方便的获取到底层的字节操作和字节数据. 比如你有一个int型数据, 而你想获取他的byte[]数组, 你可以这样做:
ByteBuffer buffer = ByteBuffer.allocate(1024); //分配一定的空间,1024
int i = 90;
buffer.putInt(i);
byte[] array = buffer.array(); //获取该buffer的数组,这个数组是跟该buffer一一对应的
for(int j =0; j <4;j++){
System.out.println(Integer.toBinaryString(array[j] & 0xFF));
}
fanxu 评论于 2010-09-16 16:59  回复  更多评论   
# public static ByteBuffer wrap(byte[] array)
将array中的数据包装到ByteBuffer中
byte[] array = new byte[1024];
ByteBuffer buffer = ByteBuffer.wrap(array);

等效于
ByteBuffer buffer = ByteBuffer.allocate(1024); //分配一定的空间,1024
fanxu 评论于 2010-09-16 17:03  回复  更多评论   
# flip
public final Buffer flip()
反转此缓冲区。首先对当前位置设置限制,然后将该位置设置为零。如果已定义了标记,则丢弃该标记。
当将数据从一个地方传输到另一个地方时,经常将此方法与 compact 方法一起使用。

我最终的理解是: 文档翻译得太差了, 把不应该翻译的内容也译成了中文, 所以反而不容易理解.
关键就在以下 2 处:

当前位置: 这个可以直观地理解为缓冲区中的当前数据指针, 或是 SQL 中的游标, 记为 curPointer.
限制: 这个可以理解成实际操作的缓冲区段的结束标记, 记为 endPointer.
反转: 这个完全是对 flip 这个词不负责的翻译, 如果参照 DirectX 里的 flip() 而译为翻转/翻页, 那就好理解得多, 就像写信/看信, 写/看完一页后, 翻到下一页, 眼睛/笔从页底重新移回页首.
这个翻转背后的操作其实就是 "把 endPointer 定位到 curPointer 处, 并把 curPointer 设为 0".

关于标记, 在这里不涉及. 下一句说到常与 compact 方法一起使用, 是可以想像的, 因为 compact 方法对数据进
行了压缩, 有效数据的真实长度发生了变化, 肯定需要用 flip 重新定位结束标记.

在填充, 压缩等数据操作时, curPointer 估计都是自动更新了位置的, 总是指向最后一个有效数据, 所以每次调
用 flip() 后, endPointer 就指向了有效数据的结尾, 而 curPointer 指向了 0 (缓冲起始处).

举个图例:
(c 和 e 分别代表 curPointer 和 endPointer 两个指针)

* 先是一个空的 ByteBuffer (大小为 10 字节)
-------------------
-------------------
c
e


* 然后填充 5 字节数据
-------------------
0 1 2 3 4
-------------------
e c
此时, endPointer 尚在 0 处, curPointer 移到了数据结尾.
经测试, 此时若取数据, 将得到 5 个字节, 内容通常为 0 (也有可能是未知), 因为实际上取到的是从 c 处到缓冲
区实际结束处的 5 个未初始化的字节.
(QZone 字体处理不正确, 此处 c 是在 4 的下面, e 在 0 的下面)

* 调用一次 flip() 后
-------------------
0 1 2 3 4
-------------------
c e
此时, endPointer 先被移到 curPointer, 然后 curPointer 移到 0.
通过测试可见, ByteBuffer 取数据时, 是从 curPointer 起, 到 endPointer 止, 若 curPointer > endPointer, 则取到缓冲区结束.
(QZone 字体处理不正确, 此处 c 是在 0 的下面, e 在 4 的下面)

再看上面代码的关键片段, 行 8 处调用 flip() 即有两个作用, 一是将 curPointer 移到 0, 二是将 endPointer 移到有效数据结尾.

此行可由以下两行代替:
buff.limit( buff.position());
buff.position( 0 );

可见对其工作原理的理解, 应该是正确的
fanxu 评论于 2010-09-16 17:04  回复  更多评论   
# 总结
1. put 数据时, 不会自动清除缓冲区中现有的数据.
2. 每一次 get 或 put 后, curPointer 都将向缓冲区尾部移动, 移动量=操作的数据量.
3. get/put 均是从 curPointer 起, 到 curPointer + 操作的数据长度止.
4. get/put 操作中, 若 curPointer 超过了 endPointer 或缓冲区总长度, 将抛出 java.nio.BufferUnderflowException 异常.

注: curPointer 和 endPointer 只是为文中方便描述命名的, 实际分别对应到 ByteBuffer.position() 和 ByteBuffer.limit() 两个方法.
fanxu 评论于 2010-09-16 17:04  回复  更多评论   
# 关于flip
ByteBuffer 的filp函数, 将缓冲区的终止位置limit设置为当前位置, 缓冲区的游标position(当前位置)重设为0.
比如 我们有初始化一个ByteBuffer 后有
ByteBuffer buffer = ByteBuffer.allocate(1024);
这是 终止位置limit在1024, 而起始位置position在 0
如果我们添加一个数据,
buffer.putint(90);
这会使起始位置 position 移到4, 也就是说postion始终都在第一个可写字节的位置上. limit 则不会发生改变
而如果这时,我们调用
buffer.flip();
position转到0, limit转到 4 也就是原来的position 所在位置
这里的flip, 从另外一个角度上来说, 是在读数据时,操作的
然而, 如果我此时在写
buffer.putInt(90);
就会将原来的覆盖掉
如果我们在写, 这时就不行了, 就会重现问题了. 因为我们的limit是4, 我们写入数据不能超过这个limit,(当然还有capacity)
所以在写的时候,最好先清空buffer.clear();
如果真的不想清空, 也可以调用
buffer.limit(newlimit);
设置一个较大的limit, 再写入
当然不能超过capacity, 可以等于 capacity
分享到:
评论

相关推荐

    Java资源加载库Azzet.zip

    示例代码:BufferedImage img = Assets.load("http://www.google.com/logos/classicplus.png"); // loaded from website Font fnt = Assets.load("myfont.ttf", new FontInfo(32.0f)); // loaded ...

    使用 DexClassLoader 动态加载字节码文件

    【Android 逆向】类加载器 ClassLoader ( 使用 DexClassLoader 动态加载字节码文件 | 准备 DEX 字节码文件 ) https://hanshuliang.blog.csdn.net/article/details/121776627 博客源码

    mina实例1mina实例1

    3. **Buffer**:MINA提供了一种基于ByteBuffer的抽象,用于高效地处理网络数据。它可以避免不必要的内存复制,提高性能。 4. **Event-Driven**:MINA是事件驱动的,当有新的连接、数据到达或连接关闭时,都会触发...

    requirejs-proto:protobuf 的 requirejs 插件

    'ByteBuffer': '../../bower_components/byteBuffer/dist/ByteBufferAB', 'ProtoBuf': '../../bower_components/protobuf/dist/ProtoBuf', 'text': '../../bower_components/requirejs-text/text',

    netty开发入门

    1. **ByteBuf**: Netty中的缓冲区类,替代了Java NIO的ByteBuffer,提供了更高效、更易用的内存管理。 2. **Channel**: 表示一个网络连接,可以读写数据。 3. **EventLoop**: 事件循环,负责处理I/O事件并触发相应的...

    netty-netty-4.1.32.final-remark.zip

    1. **ByteBuf**:Netty 中的 ByteBuf 是一种高效的数据缓冲区,替代了传统的 Java NIO ByteBuffer。ByteBuf 支持读写索引独立,方便管理数据,并且提供了更多高级功能,如内存池和预分配。 2. **Channel** 和 **...

    Java高级知识

    - [OQL帮助文档](http://visualvm.java.net/oqlhelp.html) **1.1.4 OutOfMemory异常示例** - **HeapOutOfMemoryError** - 当堆空间不足时抛出 - **YoungGenerationOutOfMemoryError** - 新生代空间不足时抛出 -...

    NettyDemo.zip

    6. **ByteBuf**:Netty 提供的高效字节缓冲区,比 Java NIO 的 ByteBuffer 更加强大和易用。 在 "NettyDemo.zip" 中,我们可以期待看到以下示例: 1. **长连接**:Netty 支持建立持久的 TCP 长连接,这在需要频繁...

    Java开发基于多线程和NIO实现聊天室源码+项目说明(含服务端+客户端).zip

    - ByteBuffer - ProtoStuff 高性能序列化 - HttpClient连接池 - Spring依赖注入 - lombok简化POJO开发 - 原子变量 - 内置锁 - CompletionService - log4j+slf4j日志 - 实现的功能 - 登录注销 - 单聊 -...

    netty-all-4.1.28.Final.jar

    1. **ByteBuf**: Netty自定义的字节缓冲区,比Java的ByteBuffer更加强大和高效,支持读写分离,便于数据操作和内存管理。 2. **Channel**: 表示网络连接,可以是TCP连接、UDP连接或者本地套接字等。每个Channel对应...

    jetty所需jar包

    7. **jetty-io.jar**:提供了低级别的网络I/O操作,如ByteBuffer和NIO相关的工具。 8. **jetty-webapp.jar**:处理Web应用程序的部署和管理,支持WAR文件的部署和解压。 9. **jetty-jndi.jar**:如果需要JNDI...

    netty-all-4.1.5.Final:netty-all-4.1.5.Final源码学习

    6. **Buffer(ByteBuf)**:Netty自定义的高效字节缓冲区,替代了Java的ByteBuffer。ByteBuf提供了更友好的API,支持动态扩容,并且在读写操作上更加灵活。 7. **Handler(处理器)**:ChannelHandler是Pipeline中...

    jdk1.7 api文档.chm

    - **java.nio**:非阻塞I/O,提供更高效的读写操作,如`ByteBuffer`、`Channels`和`Selectors`。 4. **网络编程**: - **java.net**:处理网络连接,包括`Socket`、`ServerSocket`、`URL`和`URLConnection`,支持...

    java performance11

    ByteBuffer buffer = ByteBuffer.allocate(512); start = System.currentTimeMillis(); while (fcin11.read(buffer) != -1) { buffer.flip(); fcout11.write(buffer); buffer.clear(); } fin11.close(); fout11....

    【IT十八掌徐培成】Java基础第26天-05.ByteBuffer-mark-pos-limit-cap-flip.zip

    在Java编程语言中,`ByteBuffer`是Java NIO(New IO)框架中的核心类之一,它提供了一种高效处理字节数据的方式。本教程重点讲解了`ByteBuffer`的几个关键属性:mark、position、limit和capacity,以及重要的操作...

    卫斯理合集 倪匡

    ByteBuffer wrap = ByteBuffer.wrap("\r\n".getBytes()); outChannel.write(wrap); FileChannel fc = new FileInputStream(f).getChannel(); ByteBuffer bb = ByteBuffer.allocate(BUFSIZE); while(fc.read(bb)...

    Netty-4.1.97.Final源码

    Netty中的ByteBuf是高效的数据缓冲区,它比Java NIO的ByteBuffer更加强大和灵活。ByteBuf支持读写索引,可以动态扩容,并且提供了多种便捷的读写方法,使得处理二进制数据变得简单。 此外,Netty在协议支持方面非常...

    test_surface_test_

    import java.nio.ByteBuffer;import android.app.Activity;import android.media.MediaCodec;import android.media.MediaCodec.BufferInfo;import android.media.MediaCodecInfo;import android.media....

    Java核心技术大全

    - Java NIO(Non-blocking I/O)提供了更高效的I/O操作方式,通过`java.nio.*`包下的类如`ByteBuffer`、`Selector`等,可以实现异步非阻塞的I/O操作。 **1.2.7 Java反射与javassist** - Java反射允许程序在运行时...

Global site tag (gtag.js) - Google Analytics