Netty基础01-NIO-ByteBuffer
2、ByteBuffer
简单案例
- pom
  <dependencies>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.39.Final</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.18</version>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.5</version>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>19.0</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
    </dependencies>
- code
// 读取data.txt文件中的内容
@Slf4j
public class ChannelAndBuffer {
    public static void main(String[] args) {
        // 1、FileChannel
        // 输入输出流  RadomAccessFile
        try (FileChannel channel = new FileInputStream("data.txt").getChannel()) {
            //2、准备缓冲区 10个字节
            ByteBuffer buffer = ByteBuffer.allocate(10);
            while (true) {
                // 3、从channel 读取数据 也就是想buffer写入数据
                int len = channel.read(buffer);
                // len == -1表示没有数据
                if (len == -1) {
                    break;
                }
                log.debug("--->{}", "读取的字节数:" + len);
                // 4、切换buffer模式为度模式
                buffer.flip();
                // 5、检查buffer中是否还有剩余数据
                while (buffer.hasRemaining()) {
                    byte b = buffer.get();
                    log.debug("--->{}", "读出的字节:" + (char) b);
                }
                
                // 6、切换为写模式
                buffer.clear();
            }
        } catch (IOException e) {
        }
    }
}
- 日志
2021-04-10 14:18:12.648 [main] DEBUG com.riest.netty.ChannelAndBuffer:  --->读取的字节数:10
2021-04-10 14:18:12.652 [main] DEBUG com.riest.netty.ChannelAndBuffer:  --->读出的字节:1
2021-04-10 14:18:12.652 [main] DEBUG com.riest.netty.ChannelAndBuffer:  --->读出的字节:2
2021-04-10 14:18:12.652 [main] DEBUG com.riest.netty.ChannelAndBuffer:  --->读出的字节:3
2021-04-10 14:18:12.652 [main] DEBUG com.riest.netty.ChannelAndBuffer:  --->读出的字节:4
2021-04-10 14:18:12.652 [main] DEBUG com.riest.netty.ChannelAndBuffer:  --->读出的字节:5
2021-04-10 14:18:12.653 [main] DEBUG com.riest.netty.ChannelAndBuffer:  --->读出的字节:6
2021-04-10 14:18:12.653 [main] DEBUG com.riest.netty.ChannelAndBuffer:  --->读出的字节:7
2021-04-10 14:18:12.653 [main] DEBUG com.riest.netty.ChannelAndBuffer:  --->读出的字节:8
2021-04-10 14:18:12.653 [main] DEBUG com.riest.netty.ChannelAndBuffer:  --->读出的字节:9
2021-04-10 14:18:12.653 [main] DEBUG com.riest.netty.ChannelAndBuffer:  --->读出的字节:0
2021-04-10 14:18:12.653 [main] DEBUG com.riest.netty.ChannelAndBuffer:  --->读取的字节数:3
2021-04-10 14:18:12.653 [main] DEBUG com.riest.netty.ChannelAndBuffer:  --->读出的字节:a
2021-04-10 14:18:12.653 [main] DEBUG com.riest.netty.ChannelAndBuffer:  --->读出的字节:b
2021-04-10 14:18:12.653 [main] DEBUG com.riest.netty.ChannelAndBuffer:  --->读出的字节:c
正确使用姿势
- 向buffer写入数据。例如调用channel.read(buffer)
- 调用flip() 切换至读模式
- 从buffer读取数据,例如调用buffer.get()
- 调用clear()或者compacy()切换至写模式
- 重复上述步骤
结构
- limit:写入限制
- Capacity:容量
- Position:位置(指针)
一开始:

写模式下:position是写入位置,limit等于容量,下图表示写入了4个字节后的状态

flip()动作发生后:position切换为读取位置,limit切换为读取限制

读取四个字节后,状态

clear()发生后,状态:

comapct()方法,将未读完的部分向前压缩,然后切换至写模式

常用方法
- 
分配内存方法 // 1、分配内存的方法 ByteBuffer allocate = ByteBuffer.allocate(16); ByteBuffer byteBuffer = ByteBuffer.allocateDirect(16); // class java.nio.HeapByteBuffer java 堆内存,读写效率低,收到GC影响 System.out.println(allocate.getClass()); // class java.nio.DirectByteBuffer 系统内存 ,读写效率高(少一次拷贝),不受GC影响,分配效率低 System.out.println(byteBuffer.getClass());
- 
写入方法 // 2、写入方法 try (FileChannel channel = new FileInputStream("").getChannel()) { ByteBuffer buffer = ByteBuffer.allocate(10); // channel channel.read(buffer); // buffer buffer.put((byte)'a'); //...... } catch (IOException e) { }
- 
读取方法 //3、读取方法 ByteBuffer allocate = ByteBuffer.allocate(10); // 写 allocate.put(new byte[]{'a', 'b', 'c', 'd'}); // 切换至读模式 allocate.flip(); allocate.get(new byte[4]); debugAll(allocate); // 从头开始读 allocate.rewind(); debugAll(allocate); // get 方法会让position的指针往后移动,如果向重复读取数据 // 可以将rewind方法将position重新置为0 // 或者调用get(int i)方法获取索引的 i 的内容,它不会移动指针

- 
mark & reset mark 做一个标记,记录position的位置 reset将position重置到position的位置 ByteBuffer allocate = ByteBuffer.allocate(10); allocate.put(new byte[]{'a', 'b', 'c', 'd'}); allocate.flip(); System.out.println("---->"+allocate.get()); System.out.println("---->"+allocate.get()); // 标记position的位置 allocate.mark(); System.out.println("---->"+allocate.get()); System.out.println("---->"+allocate.get()); // 将position 重置到 postion 索引为2 的位置 allocate.reset(); System.out.println("---->"+allocate.get());

- 
String 和 ByteBuffer相互转换 // 1、字符串转ByteBuffer ByteBuffer buffer1 = ByteBuffer.allocate(16); buffer1.put("hello".getBytes()); debugAll(buffer1); // 该方法会自动切换至读模式 ByteBuffer buffer2 = StandardCharsets.UTF_8.encode("hello"); debugAll(buffer2); // 该方法会自动切换至读模式 ByteBuffer buffer3 = ByteBuffer.wrap("hello".getBytes()); debugAll(buffer3); // 2、ByteBuffer 转字符串 String str1 = StandardCharsets.UTF_8.decode(buffer2).toString(); System.out.println("---->"+str1); // buffer1切换读模式 buffer1.flip(); String str2 = StandardCharsets.UTF_8.decode(buffer1).toString(); System.out.println("---->"+str2);

分散读取Scattered Read
    /**
     * 分散读取
     */
    public  static  void scatteringRead(){
        try (FileChannel channel = new RandomAccessFile("word1.txt","r").getChannel()) {
            ByteBuffer buffer1 = ByteBuffer.allocate(4);
            ByteBuffer buffer2 = ByteBuffer.allocate(3);
            ByteBuffer buffer3 = ByteBuffer.allocate(6);
            channel.read(new ByteBuffer[]{buffer1,buffer2,buffer3});
            buffer1.flip();
            buffer2.flip();
            buffer3.flip();
            debugAll(buffer1);
            debugAll(buffer2);
            debugAll(buffer3);
        } catch (IOException e) {
        }
    }

集中写 Gather Write
    /**
     * 集中写
     */
    public static void gatherWrite(){
        ByteBuffer b1 = StandardCharsets.UTF_8.encode("NIO ");
        ByteBuffer b2 = StandardCharsets.UTF_8.encode("IS  ");
        ByteBuffer b3 = StandardCharsets.UTF_8.encode("SOEASY");
        try (FileChannel channel = new RandomAccessFile("word2.txt", "rw").getChannel()) {
            channel.write(new ByteBuffer[]{b1,b2,b3});
        } catch (IOException e) {
        }
    }

练习
网络上有很多条数据发送给服务器,数据之间使用 \n 进行分隔 ,但由于某种原有导致这些数据在接收是,被进行了重新组合,例如原始数据有三条,分别为
Hello,world\n
I`m zhangsan\n
How are you?\n
变成了下面的两个ByeBuffer(黏包、半包)
Hello,world\nI`m zhangsan\nHo
w are you?\n
现在要求你编写code,将错乱的数据恢复成原始的按照 \n分隔的数据
code 如下:
    public static void main(String[] args) {
        String str1 = "hello,world\nI`m zhangsan\nHo";
        String str2 = "w are you?\n";
        ByteBuffer b1 = ByteBuffer.allocate(30);
        b1.put(str1.getBytes());
        dispose(b1);
        b1.put(str2.getBytes());
        dispose(b1);
    }
    public static void dispose(ByteBuffer buffer) {
        // 切换成读模式
        buffer.flip();
        for (int i = 0; i < buffer.limit(); i++) {
            if (buffer.get(i) == '\n') {
                // \n 的位置
                int len = i + 1 - buffer.position();
                ByteBuffer allocate = ByteBuffer.allocate(len);
                for (int j = 0; j < len; j++) {
                    allocate.put(buffer.get());
                }
                debugAll(allocate);
            }
        }
        //将未读完的部分向前压缩,然后切换至写模式
        buffer.compact();
    }

 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号