首页 > 其他分享 >今日博客

今日博客

时间:2022-08-21 23:15:13浏览次数:59  
标签:get Buffer 博客 buffer source ByteBuffer position 今日

3.2、Buffer的capacity,position和limit

image-20220821155010922

capacity

作为一个内存块,Buffer有一个固定的大小值,也叫“capacity”.

position

当你写数据到Buffer中时,position表示当前的位置。

当将Buffer从写模式切换到读模式(flip()),position会被重置为0.

当Buffer切换回写模式时(clear()),position重置为0.

当Buffer切换回写模式时(compact()),compact()只会清除已经读过的数据。任何未读过的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面,position重置为缓冲区未读数据的后面.

limit

在写模式下,Buffer的limit表示你最多能往Buffer里写多少数据。

当切换Buffer到读模式时(flip()), limit表示你最多能读到多少数据。因此,当切换Buffer到读模式时,limit会被设置成写模式下的position值。

当Buffer切换回写模式时(clear()),limit重置为capacity.

当Buffer切换回写模式时(compact()),limit重置为capacity.

3.3、Buffer的类型

  • ByteBuffer
    • MappedByteBuffer
    • DirectByteBuffer
    • HeapByteBuffer
  • CharBuffer
  • DoubleBuffer
  • FloatBuffer
  • IntBuffer
  • LongBuffer
  • ShortBuffer

3.4、Buffer的分配

System.out.println(ByteBuffer.allocate(16).getClass()); // 16字节
System.out.println(ByteBuffer.allocateDirect(16).getClass());
// class java.nio.HeapByteBuffer - java 堆内存,读写效率较低,受到 GC 影响
// class java.nio.DirectByteBuffer - 直接内存,读写效率高(少一次拷贝),不会受GC影响,分配的效率低

3.5、向Buffer中写数据

写数据到Buffer有两种方式:

  • 从Channel写到Buffer。

    int bytesRead = channel.read(buf); //read into buffer.
    
  • 通过Buffer的put()方法写到Buffer里。

    buf.put(127);
    

3.6、从Buffer中读取数据

从Buffer中读取数据有两种方式:

  1. 从Buffer读取数据到Channel。

    int bytesWritten = inChannel.write(buf);
    
  2. 使用get()方法从Buffer中读取数据。

    byte aByte = buf.get();
    

3.7、字符串与Buffer互转

字符串转为Buffer

// 1. 字符串转为ByteBuffer,需要手动转换为读模式
ByteBuffer buffer = ByteBuffer.allocate(16);
buffer.put("hello".getBytes());

// 打印buffer中的内容
buffer.flip();
while (buffer.hasRemaining()) {
    System.out.println((char)buffer.get());
}
buffer.clear();

// 2. Charset,会自动切换为读模式
ByteBuffer buffer2 = StandardCharsets.UTF_8.encode("hello");

// 打印buffer中的内容
while (buffer2.hasRemaining()) {
    System.out.println((char)buffer2.get());
}
buffer2.clear();

// 3. wrap,会自动切换为读模式
ByteBuffer buffer3 = ByteBuffer.wrap("hello".getBytes());

// 打印buffer中的内容
while (buffer2.hasRemaining()) {
    System.out.println((char)buffer2.get());
}
buffer2.clear();

Buffer转为字符串

String str2 = StandardCharsets.UTF_8.decode(buffer2).toString(); // 自动切换为读模式
System.out.println(str2);
String str3 = StandardCharsets.UTF_8.decode(buffer3).toString(); // 自动切换为读模式
System.out.println(str3);

buffer.flip(); // 切换为读模式
String str = StandardCharsets.UTF_8.decode(buffer).toString(); 
System.out.println(str);

3.8、Buffer的一些方法

rewind()方法

Buffer.rewind()将position设回0,所以你可以重读Buffer中的所有数据。limit保持不变,仍然表示能从Buffer中读取多少个元素(byte、char等)。

get方法会让position往后走,如果想重复的读取数据:

  • 可以调用rewind方法将position重新置为0
  • 或者调用get(int i)方法获取索引i的内容,他不会移动position

mark()与reset()方法

通过调用Buffer.mark()方法,可以标记Buffer中的一个特定position。之后可以通过调用Buffer.reset()方法恢复到这个position。

buffer.mark(); // 加标记
buffer.reset(); // 将position重置到 标记处

get(i)

调用get(int i)方法获取索引i的内容,他不会改变position的位置

4、Scatter/Gather

分散度集中写,针对Channel

Java NIO开始支持scatter/gather,scatter/gather用于描述从Channel中读取或者写入到Channel的操作

分散(scatter)从Channel中读取是指在读操作时将读取的数据写入多个buffer中。因此,Channel将从Channel中读取的数据“分散(scatter)”到多个Buffer中。
聚集(gather)写入Channel是指在写操作时将多个buffer的数据写入同一个Channel,因此,Channel 将多个Buffer中的数据“聚集(gather)”后发送到Channel。

scatter / gather经常用于需要将传输的数据分开处理的场合,例如传输一个由消息头和消息体组成的消息,你可能会将消息体和消息头分散到不同的buffer中,这样你可以方便的处理消息头和消息体。

4.1、Scattering Reads

try (FileChannel channel = new RandomAccessFile("data/nio-data.txt", "r").getChannel()) {
    ByteBuffer b1 = ByteBuffer.allocate(10);
    ByteBuffer b2 = ByteBuffer.allocate(3);
    ByteBuffer b3 = ByteBuffer.allocate(3);
    channel.read(new ByteBuffer[]{b1,b2,b3});

    //打印b1,b2,b3的内容
    printBuffer(b1);
    printBuffer(b2);
    printBuffer(b3);

} catch (IOException e) {
}

private static void printBuffer(ByteBuffer buffer) {
    buffer.flip();
    while (buffer.hasRemaining()) {
        System.out.println((char) buffer.get());
    }
    buffer.clear();
}

Scattering Reads在移动下一个buffer前,必须填满当前的buffer,这也意味着它不适用于动态消息(译者注:消息大小不固定)。换句话说,如果存在消息头和消息体,消息头必须完成填充(例如 128byte),Scattering Reads才能正常工作。

4.2、Gathering Writes

ByteBuffer buffer1 = StandardCharsets.UTF_8.encode("hello1");
ByteBuffer buffer2 = StandardCharsets.UTF_8.encode("hello2");
ByteBuffer buffer3 = StandardCharsets.UTF_8.encode("hello3");
try (FileChannel channel = new RandomAccessFile("data/nio-data.txt", "rw").getChannel()) {
    channel.write(new ByteBuffer[]{buffer1,buffer2,buffer3});
} catch (IOException e) {
}

注意只有position和limit之间的数据才会被写入。因此,如果一个buffer的容量为128byte,但是仅仅包含58byte的数据,那么这58byte的数据将被写入到channel中。

5、粘包/半包分析

例题:网络上有多条数据发送给服务器,数据之间使用 \n 进行分割

但由于某种原因这些数据在接收时,被进行了重新组合,假如原始数据有3条:

​ Hello,world\n

​ I'm Wumin\n

​ How are you?\n

变成了下面的两个 ByteBuffer (粘包,半包

​ Hello,world\nI'm Wumin\nHo

​ w are you?\n

  • 粘包:两条消息合在一起。发送消息时,一次将全部消息发送出去,就可能产生黏包问题。

  • 半包:消息被截断。半包主要是因为服务器缓冲区导致的,放不下了,所以就会产生半包。

现在要求你编写程序,将错乱的数据恢复成原始的按 \n 分隔的数据

public class TestBufferExam {
    public static void main(String[] args) {
        /**
         * 网络上有多条数据发送给服务器,数据之间使用 \n 进行分割
         *
         * 但由于某种原因这些数据在接收时,被进行了重新组合,假如原始数据有3条:
         *
         *        Hello,world\n
         *
         *         I'm Wumin\n
         *
         *        How are you?\n
         *
         * 变成了下面的两个 ByteBuffer (==粘包,半包==)
         *
         *        Hello,world\nI'm Wumin\nHo
         *
         *        w are you?\n
         * 现在要求你编写程序,将错乱的数据恢复成原始的按 \n 分隔的数据
         */

        ByteBuffer source = ByteBuffer.allocate(40);
        source.put("Hello,world\nI'm Wumin\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 target = ByteBuffer.allocate(length);
                for (int j = 0; j < length; j++) {
                    target.put(source.get());
                }
                System.out.println(source.position());
                printBuffer(target);
            }
        }
        source.compact();
    }

    private static void printBuffer(ByteBuffer buffer) {
        buffer.flip();
        while (buffer.hasRemaining()) {
            System.out.print((char) buffer.get());
        }
        buffer.clear();
    }
}

6、FileChannel

FileChannel无法设置为非阻塞模式,它总是运行在阻塞模式下

我们无法直接打开一个FileChannel,需要通过使用一个InputStream、OutputStream或RandomAccessFile来获取一个FileChannel实例,他们都有getChannel()方法

  • 通过InputStream获取的channel只能读
  • 通过OutputStream获取的channel只能写
  • 通过RandomAccessFile获取的channel根据构造RandomAccessFile时的读写模式决定

标签:get,Buffer,博客,buffer,source,ByteBuffer,position,今日
From: https://www.cnblogs.com/wuminsxd/p/16611338.html

相关文章

  • 给你的博客加个aplayer
    1.在layout.ejs中body标签内粘贴入以下<!--音乐--><linkrel="stylesheet"href="https://cdn.jsdelivr.net/npm/[email protected]/dist/APlayer.min.css"><scr......
  • 博客自定义右键
    相信大家在访问我的网站就发现我的右键已经更换,那么今天就介绍一下我的方法!1.首先在head.ejs内添加:<!--自定义右键--></script><styletype="text/css">a{text-d......
  • 博客园主题推荐awescnb-自定义
    后台-设置博客皮肤:Custom页面定制CSS代码#loading{bottom:0;left:0;position:fixed;right:0;top:0;z-index:9999;background-color:......
  • Github + Hexo 搭建个人博客超详细教程
    本文目录generatedwithDocToc网站搭建本文目录1.安装node.js2.添加国内镜像3.安装Git4.注册Github账号5.创建git仓库6.安装Hexo7.配置本地hexo8.连接Github......
  • MarkDown 本地图片快速上传到博客园
    到.NETDownloads下载.NET5打开CMD之类的终端,运行dotnettoolinstall--globaldotnet-cnblog安装dotnet-cnblogs-tool到博客后台创建Token,并复制运行......
  • Hexo+GitHub搭建个人博客
    操作环境:Windows10、Node、Git、ssh前置准备:<username>github.io仓库已建立,预计托管博客网址为<username>github.io/blog先对hexo有个清晰的认识,不至于稀里糊涂的跟......
  • 解决Notepad++ 中写的代码粘贴到博客园中,代码对不齐问题
    在Notepad++中写代码之前,在 设置->首选项..   在首选项页面上,进行如下操作,将制表符都替换为空格,就OK了  可以在视图->显示符号 -> 显示空格与制......
  • 博客搭建
    Author:meteordate:2022-08-2013:02:54LastEditTime:2022-08-2013:07:57title:博客搭建tags:hexo+github+action+pages#Thisisabasicworkflowtohelp......
  • 博客园定制皮肤收集内容1
    花了一点时间修改自己的博客背景设置,现在分享一下代码。希望对大家有帮助。主要代码模板来源于另外三个博主原博主的连接:(1)https://www.cnblogs.com/Penn000/p/6947472.ht......
  • 如何将typora的文件上传到博客园
    如何将typora的文件上传到博客园1.下载typora在偏好设置中做以下选择2.下载dotnet官网下载地址:https://dotnet.microsoft.com/下载后安装即可,window用户它会直......