NIO之ByteBuffer

NIO中的ByteBuffer是缓冲区,其中有几个比较重要的属性capacity,position和limit。

capacity:

其中,capacity是缓冲区的容量大小,在分配内存空间后不会改变。

limit:

limit是限制位置,在读写模式切换后limit的位置也跟着切换,切换到读限制位置和写限制位置。在写模式下limit一般位于capacity处,即不超出buffer的范围即可。在读模式下limit一般位于buffer中最后一个字符的位置,即在读的时候不超出存有内容的范围即可。

position:

position是写入位置或读取位置,在读写切换时也会随之切换。如写模式下随着字符的写入,position的位置会相应地后移,当切换为读模式时,position会一直最前方,此时其代表读取位置。读取操作结束后在切回写模式时,分为两种情况。第一种是使用clear进行切换,之前缓冲区里的全部元素,无论切换前是否读过,都直接抛弃,position移至buffer的最前方。另一种是compact,这种方式是保留没读过的元素,也就是读模式的position和读模式的limit之间的元素,切换写模式时会被保留,position会指向被保留元素的下一个。

如下列的一些图

一开始:

写入4字节:

flip切换为读模式,position切换到读取位置(最前方),limit切换为读取限制,有元素的最后一个位置:

读了4个字节后:

以clear切换为写模式,元素清空,limit和position也进行切换:

假设只读了两个字节,剩下的两个没读,并且是以compact方式进行的切换,那会保留没读过的元素,position和limit也切换为写模式,并且position位于保留元素的后方:

关于position还有需要注意的是, 我们在读模式下进行读取时,一般是调用buffer的get方法,这时每读一个字符指针就会后移一位。注意可以调用 rewind 方法将 position 重新置为 0,或者调用 get(int i) 方法获取索引 i 的内容,它不会移动读指针。另外,使用mark 可以在读取时做一个标记,即使 position 改变,只要调用 reset 就能回到 mark 的位置,但是需注意rewind 和 flip 都会清除 mark 位置。

实战举例,解决网络收发数据时的半包粘包情况,灵活利用了ByteBuffer读写模式的切换以及对于position的灵活应用:

java 复制代码
public static void main(String[] args) {
    ByteBuffer source = ByteBuffer.allocate(32);
    //                     11            24
    source.put("Hello,world\nI'm zhangsan\nHo".getBytes());
    split(source);

    source.put("w are you?\nhaha!\n".getBytes());
    split(source);
}

private static void split(ByteBuffer source) {
    // 切换为读模式
    source.flip();
    // buffer中容量的总大小
    int oldLimit = source.limit();
    for (int i = 0; i < oldLimit; i++) {
        // 使用source.get(i)的模式,读取位置position并不会后移
        if (source.get(i) == '\n') {
            System.out.println(i);
            // 当前位置与position间的长度是新字符串长度,position是上一个串的结尾
            ByteBuffer target = ByteBuffer.allocate(i + 1 - source.position());
            // 0 ~ limit
            source.limit(i + 1);
            // 从source 读,向 target 写,position后移到该完整串的结尾
            target.put(source); 
            debugAll(target);
            source.limit(oldLimit);
        }
    }
    // 有剩余的半包并未真正读取,切换写模式时保留,等后续内容到达补全
    source.compact();
}
相关推荐
移民找老国1 天前
加拿大移民新风向
java-ee·maven·phpstorm·visual studio code·nio
Mr.W.T4 天前
NIO 核心知识总结
io·nio
happycao1234 天前
Java NIO2 异步IO支持
java·nio
WaaTong4 天前
Java NIO 【处理消息边界】
nio
_LiuYan_15 天前
BIO,NIO,直接内存,零拷贝
linux·网络·nio
Amor风信子15 天前
SpringBoot启动报错java.nio.charset.MalformedInputException: Input length =1
java·spring boot·后端·spring·nio·1024程序员节
J老熊16 天前
Java NIO缓冲区与非阻塞机制详解和案例示范
java·开发语言·后端·面试·系统架构·nio
爱折磨键盘的大鹏17 天前
Netty初体验-1-NIO基础补漏
nio
胡耀超22 天前
JNI(Java Native Interface)和NIO(New Input/Output)是什么?
java·开发语言·nio·jni
星沁城22 天前
BIO与NIO学习
java·学习·nio