@IT·互联网

ByteBuffer

2023-12-25  本文已影响0人  我可能是个假开发

一、.基本使用

使用ByteBuffer读取文件中的内容:

public class TestByteBuffer {
    public static void main(String[] args) {
        // 获得FileChannel
        try (FileChannel channel = new FileInputStream("stu.txt").getChannel()) {
            // 获得缓冲区
            ByteBuffer buffer = ByteBuffer.allocate(10);
            int hasNext = 0;
            StringBuilder builder = new StringBuilder();
            while((hasNext = channel.read(buffer)) > 0) {
                // 切换模式 limit=position, position=0
                buffer.flip();
                // 当buffer中还有数据时,获取其中的数据
                while(buffer.hasRemaining()) {
                    builder.append((char)buffer.get());
                }
                // 切换模式 position=0, limit=capacity
                buffer.clear();
            }
            System.out.println(builder.toString());
        } catch (IOException e) {
        }
    }
}

二、结构

bytebuffer.png
public class TestByteBufferReadWrite {
    public static void main(String[] args) {
        ByteBuffer buffer = ByteBuffer.allocate(10);
        buffer.put((byte) 0x61); // 'a'
        debugAll(buffer);
        /**
         * position: [1], limit: [10]
         *          +-------------------------------------------------+
         *          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
         * +--------+-------------------------------------------------+----------------+
         * |00000000| 61 00 00 00 00 00 00 00 00 00                   |a.........      |
         * +--------+-------------------------------------------------+----------------+
         */

        buffer.put(new byte[]{0x62, 0x63, 0x64}); // b  c  d
        debugAll(buffer);
        /**
         * position: [4], limit: [10]
         *          +-------------------------------------------------+
         *          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
         * +--------+-------------------------------------------------+----------------+
         * |00000000| 61 62 63 64 00 00 00 00 00 00                   |abcd......      |
         * +--------+-------------------------------------------------+----------------+
         */

        //0 不切换成读模式,读不到数据
//        System.out.println(buffer.get());
        buffer.flip();
        //97
        System.out.println(buffer.get());
        debugAll(buffer);
        /**
         * position: [1], limit: [5]
         *          +-------------------------------------------------+
         *          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
         * +--------+-------------------------------------------------+----------------+
         * |00000000| 61 62 63 64 00 00 00 00 00 00                   |abcd......      |
         * +--------+-------------------------------------------------+----------------+
         */

        buffer.compact();
        debugAll(buffer);
        /**
         *  position: [3], limit: [10]
         *          +-------------------------------------------------+
         *          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
         * +--------+-------------------------------------------------+----------------+
         * |00000000| 62 63 64 64 00 00 00 00 00 00                   |bcdd......      |
         * +--------+-------------------------------------------------+----------------+
         * +--------+-------------------- all ------------------------+----------------+
         */


        buffer.put(new byte[]{0x65,0x66});
        debugAll(buffer);
        /**
         *  position: [5], limit: [10]
         *          +-------------------------------------------------+
         *          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
         * +--------+-------------------------------------------------+----------------+
         * |00000000| 62 63 64 65 66 00 00 00 00 00                   |bcdef.....      |
         * +--------+-------------------------------------------------+----------------+
         */
    }
}

三、常见方法

1.分配空间

可以使用 allocate 方法为 ByteBuffer 分配空间,其它 buffer 类也有该方法

import java.nio.ByteBuffer;

public class TestByteBufferAllocate {
    public static void main(String[] args) {
        System.out.println(ByteBuffer.allocate(16).getClass()); // class java.nio.HeapByteBuffer
        System.out.println(ByteBuffer.allocateDirect(16).getClass()); // class java.nio.DirectByteBuffer
    }
}

2.向 buffer 写入数据

3.从 buffer 读取数据

get 方法会让 position 读指针向后走,如果想重复读取数据

4.mark 和 reset

mark 是在读取时,做一个标记,即使 position 改变,只要调用 reset 就能回到 mark 的位置

public class TestByteBufferRead {

    public static void main(String[] args) {
        ByteBuffer buffer = ByteBuffer.allocate(10);
        buffer.put(new byte[]{'a', 'b', 'c', 'd'});
        buffer.flip();

        // rewind 从头开始读
        /*
        buffer.get(new byte[4]);
        debugAll(buffer);
        buffer.rewind();
        System.out.println((char)buffer.get());
        */
        /**
         * position: [4], limit: [4]
         *          +-------------------------------------------------+
         *          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
         * +--------+-------------------------------------------------+----------------+
         * |00000000| 61 62 63 64 00 00 00 00 00 00                   |abcd......      |
         * a
         */

        // mark & reset
        // mark 做一个标记,记录 position 位置, reset 是将 position 重置到 mark 的位置
        /*
        System.out.println((char) buffer.get()); //a
        System.out.println((char) buffer.get()); //b
        buffer.mark(); // 加标记,索引 2 的位置
        System.out.println((char) buffer.get()); //c
        System.out.println((char) buffer.get()); //d
        buffer.reset(); // 将 position 重置到做标记的地方,即索引 2
        System.out.println((char) buffer.get()); //c
        System.out.println((char) buffer.get()); //d
        */

        // get(i) 不会改变读索引的位置
        System.out.println((char) buffer.get(3)); //d
        debugAll(buffer);
        /**
         * position: [0], limit: [4]
         *          +-------------------------------------------------+
         *          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
         * +--------+-------------------------------------------------+----------------+
         * |00000000| 61 62 63 64 00 00 00 00 00 00                   |abcd......      |
         */
    }
}

rewind 和 flip 都会清除 mark 位置

四、字符串与 ByteBuffer 互转

public class TestByteBufferString {
    public static void main(String[] args) {
        // 1. 字符串转为 ByteBuffer
        ByteBuffer buffer1 = ByteBuffer.allocate(16);
        buffer1.put("hello".getBytes());
        debugAll(buffer1);
        /**
         * position: [5], limit: [16]
         *          +-------------------------------------------------+
         *          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
         * +--------+-------------------------------------------------+----------------+
         * |00000000| 68 65 6c 6c 6f 00 00 00 00 00 00 00 00 00 00 00 |hello...........|
         */

        // 2. Charset 会自动切换到读模式
        ByteBuffer buffer2 = StandardCharsets.UTF_8.encode("hello");
        debugAll(buffer2);
        /**
         * position: [0], limit: [5]
         *          +-------------------------------------------------+
         *          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
         * +--------+-------------------------------------------------+----------------+
         * |00000000| 68 65 6c 6c 6f                                  |hello           |
         */

        // 3. wrap 会自动切换到读模式
        ByteBuffer buffer3 = ByteBuffer.wrap("hello".getBytes());
        debugAll(buffer3);
        /**
         * position: [0], limit: [5]
         *          +-------------------------------------------------+
         *          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
         * +--------+-------------------------------------------------+----------------+
         * |00000000| 68 65 6c 6c 6f                                  |hello           |
         */

        // 4. 转为字符串 读模式下可以直接转 写模式下找不到数据
        String str1 = StandardCharsets.UTF_8.decode(buffer2).toString();
        System.out.println(str1);//hello

        // 写模式下需要切换为读模式
        buffer1.flip();
        String str2 = StandardCharsets.UTF_8.decode(buffer1).toString();
        System.out.println(str2);//hello

    }
}

五、Scattering Reads

分散读取,文本文件 3parts.txt
onetwothree

public class TestScatteringReads {
    public static void main(String[] args) {
        try (FileChannel channel = new RandomAccessFile("words2.txt", "r").getChannel()) {
            ByteBuffer b1 = ByteBuffer.allocate(3);
            ByteBuffer b2 = ByteBuffer.allocate(3);
            ByteBuffer b3 = ByteBuffer.allocate(5);
            channel.read(new ByteBuffer[]{b1, b2, b3});
            b1.flip();
            b2.flip();
            b3.flip();
            debugAll(b1);
            debugAll(b2);
            debugAll(b3);
        } catch (IOException e) {
        }
    }
}
position: [0], limit: [3]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 68 65 6c                                        |hel             |
+--------+-------------------------------------------------+----------------+
+--------+-------------------- all ------------------------+----------------+
position: [0], limit: [3]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 6c 6f 77                                        |low             |
+--------+-------------------------------------------------+----------------+
+--------+-------------------- all ------------------------+----------------+
position: [0], limit: [5]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 6f 72 6c 64 e4                                  |orld.           |

六、Gathering Writes

将多个 buffer 的数据填充至 channel

public class TestGatheringWrites {
    public static void main(String[] args) {
        ByteBuffer b1 = StandardCharsets.UTF_8.encode("hello");
        ByteBuffer b2 = StandardCharsets.UTF_8.encode("world");
        ByteBuffer b3 = StandardCharsets.UTF_8.encode("你好");

        try (FileChannel channel = new RandomAccessFile("words.txt", "rw").getChannel()) {
            channel.write(new ByteBuffer[]{b1, b2, b3});
        } catch (IOException e) {
        }
    }
}

减少数据拷贝,提高效率

七、网络数据传输粘包和半包

粘包:一次性发送多条数据
半包:接收方缓冲区大小限制

网络上有多条数据发送给服务端,数据之间使用 \n 进行分隔
但由于某种原因这些数据在接收时,被进行了重新组合。
将错乱的数据恢复成原始的按 \n 分隔的数据

public class TestByteBufferExam {
    public static void main(String[] args) {
        /*
        例如原始数据有3条为
             Hello,world\n
             I'm zhangsan\n
             How are you?\n
             
        变成了下面的两个 byteBuffer (黏包,半包)
             Hello,world\nI'm zhangsan\nHo
             w are you?\n
        
        将错乱的数据恢复成原始的按 \n 分隔的数据
         */
        ByteBuffer source = ByteBuffer.allocate(32);
        source.put("Hello,world\nI'm zhangsan\nHo".getBytes());
        split(source);
        source.put("w are you?\n".getBytes());
        split(source);
    }

    private static void split(ByteBuffer source) {
        source.flip();
        for (int i = 0; i < source.limit(); i++) {
            // 找到一条完整消息
            if (source.get(i) == '\n') {
                int length = i + 1 - source.position();
                // 把这条完整消息存入新的 ByteBuffer
                ByteBuffer target = ByteBuffer.allocate(length);
                // 从 source 读,向 target 写
                for (int j = 0; j < length; j++) {
                    target.put(source.get());
                }
                debugAll(target);
            }
        }
        source.compact();
    }
}
上一篇 下一篇

猜你喜欢

热点阅读